Forgot to add streamID info to match on.

if (typeof(std) == 'undefined')
    std = { };

if (typeof(std.messaging) != 'undefined')
    throw 'Error.  Already included messaging library.  Aborting instead of re-including.';

/**
 General idea: We want to support message pattern where we send a message as
 well as wait for a response for some time.  If we receive response within that
 time, we want to fire a user-defined "response" callback for that function.  If
 we do not receive a response within that time, then we fire a user-defined "no
 response" callback.
 */

(function()
{
    var DEFAULT_TIME_TO_WAIT = 5;

    std.messaging ={};
    system.require('std/shim/wrappedSendMessage/seqNumManager.em');

    /**
     @param {object} msgToSend.  An object that we want to send to
     receiver.
     @param{visible} receiver.  A visible that we want to receive the
     message;
     @param {number} optional.  A number identifying what stream
     */
    function MessageReceiverPair(msgToSend,receiver, streamID)
    {
        this.msg = msgToSend;
        this.receiver = receiver;
        if (typeof(streamID) == 'undefined')
            this.streamID  = generateNewStreamID();
        else
            this.streamID = streamID;
    }

    var uniqueIDIndex = 0;
    function generateNewStreamID()
    {
        return uniqueIDIndex ++;
    }

    /**
     @param {any} toCheck is any variable.
     @return {bool} Returns true if toCheck is a visible object.  Otherwise, returns false.
     */
    function checkIsVisible(toCheck)
    {
        if ((typeof(toCheck) != 'object')  || (toCheck == null))
            return false;

        if (! ('__getType' in toCheck))
            return false;

        if (typeof(toCheck['__getType']) != 'function')
            return false;

        return (toCheck.__getType() == 'visible');
    }

    function checkIsPresence(toCheck)
    {
        if ((typeof(toCheck) != 'object')  || (toCheck == null))
            return false;

        if (! ('__getType' in toCheck))
            return false;

        if (typeof(toCheck['__getType']) != 'function')
            return false;

        return (toCheck.__getType() == 'presence');
    }

    /**
     @param {any} Variable to check if is a MessageReceiverPair
     object.

     @return {bool} Returns true if objToCheck is a
     MessageReceiverPair, returns false otherwise.
     */
    function checkIsMessageReceiverPair(objToCheck)
    {
        if ((typeof(objToCheck) != 'object') || (objToCheck == null))
            return false;

        return (objToCheck.constructor.toString().indexOf('function MessageReceiverPair') != -1);
    }

    /**
     @param {any} Variable to check if is a MessageReceiverSender
     object.

     @return {bool} Returns true if objToCheck is a
     MessageReceiverSender, returns false otherwise.
     */
    function checkIsMessageReceiverSender(objToCheck)
    {
        if ((typeof(objToCheck) != 'object') || (objToCheck == null))
            return false;

        return (objToCheck.constructor.toString().indexOf('MessageReceiverSender') != -1);
    }

    /**
     @param {any} toCheck.  Variable to check if it is an arrray.
     @return {bool} Returns true if toCheck is an array.  False otherwise.
     */
    function checkIsArray(toCheck)
    {
        if ((typeof(toCheck) != 'object') || (toCheck == null))
            return false;

        return (toCheck.constructor.toString().indexOf('Array') != -1);
    }

    /**
     presence : mrp;
     returns this object
     (note: can use msg>> recevier to construct mrp);

     @param {presence} sender: presence sending the message from.
     @param {MessageReceiverPair} mrp.  The paired message to send and
     receiver to send that message to.
     */
    function MessageReceiverSender(sender,mrp)
    {
        if (!(checkIsPresence(sender) && checkIsMessageReceiverPair(mrp)))
            throw 'Error constructing MessageReceiverSender object.  Requires sender to be presence and mrp to be a messagereceiverpair object.';

        this.mrp = mrp;
        this.sender = sender;
    }

    std.messaging.MessageReceiverSender = function(sender,mrp)
    {
        return MessageReceiverSender(sender,mrp);
    };

    /**
     presence : mrp;
     returns this object
     (note: can use msg>> recevier to construct mrp);

     @param {presence} sender: presence sending the message from.
     @param {MessageReceiverPair} mrp.  The paired message to send and
     receiver to send that message to.
     */
    std.messaging.MessageReceiverSender = function(sender,mrp)
    {
        if (!(checkIsPresence(sender) && checkIsMessageReceiverPair(mrp)))
            throw 'Error constructing MessageReceiverSender object.  Requires sender to be presence and mrp to be a messagereceiverpair object.';

        this.mrp = mrp;
        this.sender = sender;
    };

    /**
     Turns message receiver pair into a message receiver sender object
     (using system.self as sender) and then calls down to
     sendMessageReceiverSender, which performs additional type check
     on responseArray and actually sends the message.
     */
    function callSendMessageReceiverPair(mrp,responseArray)
    {

        var mrs = new std.messaging.MessageReceiverSender(system.self,mrp);
        return callSendMessageReceiverSender(mrs,responseArray);
    }

    /**
     Does some basic checks on the responseArray (has 3 or less
     fields, has functions in first and last slots and a number in the
     middle, etc.
     */
    function callSendMessageReceiverSender(mrs, responseArray)
    {
        if (responseArray.length > 3)
            throw 'Error: Incorrectly formatted response array.  Response array requires three arguments or fewer 1) function to call on responses to your message; 2) amount of time to wait before stop listening for response; 3) function to execute if receive no response after this time.';

        var respFunc   = function(){};
        var timeToWait = DEFAULT_TIME_TO_WAIT;
        var noRespFunc = function(){};
        if (responseArray.length >= 1)
        {
            respFunc = responseArray[0];
            if (typeof(respFunc) != 'function')
                throw 'Error: Response function must be a function';
        }
        if (responseArray.length >=2)
        {
            timeToWait = responseArray[1];
            if (typeof(timeToWait) != 'number')
                throw 'Erorr: time to wait for response must be a number';
        }
        if (responseArray.length >= 3)
        {
            noRespFunc = responseArray[2];
            if (typeof(noRespFunc) != 'function')
                throw 'Error: Third arg in response array must be a function';
        }

        return std.messaging.sendMessage(mrs.mrp.msg, mrs.mrp.receiver,mrs.sender,respFunc,timeToWait,noRespFunc, mrs.mrp.streamID);
    }

    /**
     lhs >> rhs;

     @lhs {object} message to send

     @rhs {visible} receiver to send message to.

     @return {MessageReceiverPair} Returns a message receiver pair
     object.  This object can later be used to actually send the
     message.  (eg. presToSendFrom : message_receiver_pair >> []; See
     alternate arguments to understand message_receiver_pair >> []; )

     or

     @lhs {MessageReceiverPair} An object that contains both the
     message to send and the receiver that is supposed to receive it.
     In these cases, message will be sent from system.self.

     @rhs {array} A response array.  Can contain up to 3 fields.
     rhs[0] is a function to execute when receive a response.  rhs[1]
     is a number representing the number of seconds to wait before
     stopping listening for a response.  rhs[2] is a function that
     gets executed when we have not received a response within rhs[1]
     seconds.

     @return {ClearObject} @see return type for
     std.messaging.sendMessage.

     or

     @lhs {MessageReceiverSender} An object containing a
     MessageReceiverPair as well as holding

     @rhs (see above rhs)

     @return {ClearObject} Same as above.  @see return type for
     std.messaging.sendMessage.

     */
    std.messaging.sendSyntax = function (lhs, rhs)
    {
        if (checkIsMessageReceiverPair(lhs) && checkIsArray(rhs))
            return callSendMessageReceiverPair(lhs,rhs);
        else if (checkIsMessageReceiverSender(lhs) && checkIsArray(rhs))
            return callSendMessageReceiverSender(lhs,rhs);
        else if ((typeof(lhs) == 'object') && (checkIsVisible(rhs)))
            return new MessageReceiverPair(lhs,rhs);
        //lkjs;

        throw 'Error in sender syntax.  Require either that: 1) lhs must be senderReceiverPair and rhs must contain message handling code; or 2) lhs must be object and rhs must be visible.  Aborting.';
    };

    /**
     Map.  Keys: receiver.toString + '---' + sender.toString + '|||' +
     seqNo.toString(); Values: An array.  First field of array contains the
     handler registered to listen for the response.  The second field contains
     the timeout registered to fire if haven't received a response in a certain
     period of time.
     */
    var openHandlers ={   };

    /**
     @param {string} recString the string address representing the receiver of
     the message that we're sending.
     @param {string} senderString the string address representing the sender of
     the message that we're sending.
     @param {int} seqNo. The sequence number that we're sending the message off
     with.

     @return Returns a key contructed from these three values that can be used
     to access the respHandler and onNoRespTimer stored in openHandlers.
     */
    function generateKey(recString,senderString,seqNo)
    {
        return recString + '---' + senderString + '|||' + seqNo.toString();
    }

    /**
     For description of recString,senderString, and seqNo, @see generateKey
     documentation.

     @param {handler} respHandler Handle to handler registered to fire when
     receive response message.

     @param {timeout} onNoRespTimer.  Handle to timeout registered to fire if
     haven't received a response within the proscribed time.

     @return {ClearObject} Returns an object whose methods can be used to call
     "clear", which aborts listening for the response to the message.

     Registers the key generated from recString,senderString,seqNo with value of
     a two-long array containing handles to the respHandler and onNoRespTimer.
     */
    function addOpenHandler(recString,senderString, seqNo,respHandler,onNoRespTimer)
    {
        var key = generateKey(recString,senderString,seqNo);
        openHandlers[key] = [respHandler,onNoRespTimer];

        return new ClearObject(key);
    }

    /**
     Calling send message returns this object. This object can be used
     to cancel listening for responses to the message that was sent
     out.
     */
    function ClearObject(key)
    {
        var hasCanceled = false;
        this.key = key;
        this.clear = function()
        {
            if (hasCanceled)
                throw 'Error.  Cannot clear a message stream that was already cancelled.';
            cancelOpenHandler(key);
        };

        this.isCleared = function()
        {
            return hasCanceled;
        };
    }

    /**
     @see generateKey for recString,senderString, and seqNo.

     This cancels the onNoRespTimer and respHandler associated with the key
     generated from recString,senderString, and seqNo.  It also removes the key
     from openHandlers.
     */
    function cancelOpenHandler(recString,senderString,seqNo)
    {
        var key = generateKey(recString,senderString,seqNo);
        if (! (key in openHandlers))
            throw 'Error in successResponse.  Do not have key ' + key + ' in openHandlers.';

        //clear success handler
        openHandlers[key][0].clear();
        //clear timeout handler if it isn't null
        if (openHandlers[key][1] != null)
            openHandlers[key][1].clear();

        delete openHandlers[key];
    }

    std.messaging.makeReply = function(oldMsg,sndr)
    {
        var streamID = -1;
        if (typeof(oldMsg.streamID) == 'number')
            streamID = oldMsg.streamID;

        var returner = function(newMsg)
        {
            var mrp = new MessageReceiverPair(newMsg,sndr,streamID);
            return new std.messaging.MessageReceiverSender(system.self,mrp);
        };
        return returner;
    };

    /**
     @param {object} msg.  Message we want to send to receiver from sender
     @param {visible} receiver.  The future recipient of msg and.
     @param {presence} sender.  The sender of msg.
     @param {function} onResp.  A function that executes if the receiver
     responds to the message.  The function takes two parameters: the new
     message received and the sender of that message (receiver).
     @param {float} timeToWait.  The amount of time to wait before
     de-registering the onResp handler and triggering the onNoResp function.
     @param {function} onNoResp.  A function to execute if timeToWait seconds
     have gone by and we haven't received a response to our message.

     @param {number} streamID.  The id for the stream that we are sending

     @return {ClearObject} Returns an object whose methods can be used to call
     "clear", which aborts listening for the response to the message.

     */
    std.messaging.sendMessage = function (msg,receiver,sender, onResp, timeToWait,onNoResp, streamID)
    {
        msg.seqNo =     std.messaging.seqNumManager.getSeqNumber(sender,receiver,streamID);
        msg.streamID = streamID;

        //actually send the message.
        system.sendMessage(sender,msg,receiver);

        var recString = receiver.toString();
        var senderString = sender.toString();
        var seqNo = msg.seqNo;

        var wrapOnResp = function(msgRec,sndr)
        {
            cancelOpenHandler(recString,senderString,seqNo);
            onResp(msgRec,sndr);
        };

        var wrapOnNoResp = function()
        {
            cancelOpenHandler(recString,senderString,seqNo);
            onNoResp();
        };

        var respHandler    = system.registerHandler(wrapOnResp,[{'seqNo': msg.seqNo+1: }, {'streamID':streamID:}], receiver );
        var onNoRespTimeout = null;
        if (timeToWait != null)
            onNoRespTimeout = system.timeout(timeToWait,wrapOnNoResp);

        return addOpenHandler(recString,senderString,seqNo,respHandler,onNoRespTimeout);

    };

}
)();

if (typeof(std) == 'undefined')
    std = { };

if (typeof(std.messaging) != 'undefined')
    throw 'Error.  Already included messaging library.  Aborting instead of re-including.';

/**
 General idea: We want to support message pattern where we send a message as
 well as wait for a response for some time.  If we receive response within that
 time, we want to fire a user-defined "response" callback for that function.  If
 we do not receive a response within that time, then we fire a user-defined "no
 response" callback.
 */

(function()
{
    var DEFAULT_TIME_TO_WAIT = 5;

    std.messaging ={};
    system.require('std/shim/wrappedSendMessage/seqNumManager.em');

    /**
     @param {object} msgToSend.  An object that we want to send to
     receiver.
     @param{visible} receiver.  A visible that we want to receive the
     message;
     @param {number} optional.  A number identifying what stream
     */
    function MessageReceiverPair(msgToSend,receiver, streamID)
    {
        this.msg = msgToSend;
        this.receiver = receiver;
        if (typeof(streamID) == 'undefined')
            this.streamID  = generateNewStreamID();
        else
            this.streamID = streamID;
    }

    var uniqueIDIndex = 0;
    function generateNewStreamID()
    {
        return uniqueIDIndex ++;
    }

    /**
     @param {any} toCheck is any variable.
     @return {bool} Returns true if toCheck is a visible object.  Otherwise, returns false.
     */
    function checkIsVisible(toCheck)
    {
        if ((typeof(toCheck) != 'object')  || (toCheck == null))
            return false;

        if (! ('__getType' in toCheck))
            return false;

        if (typeof(toCheck['__getType']) != 'function')
            return false;

        return (toCheck.__getType() == 'visible');
    }

    function checkIsPresence(toCheck)
    {
        if ((typeof(toCheck) != 'object')  || (toCheck == null))
            return false;

        if (! ('__getType' in toCheck))
            return false;

        if (typeof(toCheck['__getType']) != 'function')
            return false;

        return (toCheck.__getType() == 'presence');
    }

    /**
     @param {any} Variable to check if is a MessageReceiverPair
     object.

     @return {bool} Returns true if objToCheck is a
     MessageReceiverPair, returns false otherwise.
     */
    function checkIsMessageReceiverPair(objToCheck)
    {
        if ((typeof(objToCheck) != 'object') || (objToCheck == null))
            return false;

        return (objToCheck.constructor.toString().indexOf('function MessageReceiverPair') != -1);
    }

    /**
     @param {any} Variable to check if is a MessageReceiverSender
     object.

     @return {bool} Returns true if objToCheck is a
     MessageReceiverSender, returns false otherwise.
     */
    function checkIsMessageReceiverSender(objToCheck)
    {
        if ((typeof(objToCheck) != 'object') || (objToCheck == null))
            return false;

        return (objToCheck.constructor.toString().indexOf('MessageReceiverSender') != -1);
    }

    /**
     @param {any} toCheck.  Variable to check if it is an arrray.
     @return {bool} Returns true if toCheck is an array.  False otherwise.
     */
    function checkIsArray(toCheck)
    {
        if ((typeof(toCheck) != 'object') || (toCheck == null))
            return false;

        return (toCheck.constructor.toString().indexOf('Array') != -1);
    }

    /**
     presence : mrp;
     returns this object
     (note: can use msg>> recevier to construct mrp);

     @param {presence} sender: presence sending the message from.
     @param {MessageReceiverPair} mrp.  The paired message to send and
     receiver to send that message to.
     */
    function MessageReceiverSender(sender,mrp)
    {
        if (!(checkIsPresence(sender) && checkIsMessageReceiverPair(mrp)))
            throw 'Error constructing MessageReceiverSender object.  Requires sender to be presence and mrp to be a messagereceiverpair object.';

        this.mrp = mrp;
        this.sender = sender;
    }

    std.messaging.MessageReceiverSender = function(sender,mrp)
    {
        return MessageReceiverSender(sender,mrp);
    };

    /**
     presence : mrp;
     returns this object
     (note: can use msg>> recevier to construct mrp);

     @param {presence} sender: presence sending the message from.
     @param {MessageReceiverPair} mrp.  The paired message to send and
     receiver to send that message to.
     */
    std.messaging.MessageReceiverSender = function(sender,mrp)
    {
        if (!(checkIsPresence(sender) && checkIsMessageReceiverPair(mrp)))
            throw 'Error constructing MessageReceiverSender object.  Requires sender to be presence and mrp to be a messagereceiverpair object.';

        this.mrp = mrp;
        this.sender = sender;
    };

    /**
     Turns message receiver pair into a message receiver sender object
     (using system.self as sender) and then calls down to
     sendMessageReceiverSender, which performs additional type check
     on responseArray and actually sends the message.
     */
    function callSendMessageReceiverPair(mrp,responseArray)
    {

        var mrs = new std.messaging.MessageReceiverSender(system.self,mrp);
        return callSendMessageReceiverSender(mrs,responseArray);
    }

    /**
     Does some basic checks on the responseArray (has 3 or less
     fields, has functions in first and last slots and a number in the
     middle, etc.
     */
    function callSendMessageReceiverSender(mrs, responseArray)
    {
        if (responseArray.length > 3)
            throw 'Error: Incorrectly formatted response array.  Response array requires three arguments or fewer 1) function to call on responses to your message; 2) amount of time to wait before stop listening for response; 3) function to execute if receive no response after this time.';

        var respFunc   = function(){};
        var timeToWait = DEFAULT_TIME_TO_WAIT;
        var noRespFunc = function(){};
        if (responseArray.length >= 1)
        {
            respFunc = responseArray[0];
            if (typeof(respFunc) != 'function')
                throw 'Error: Response function must be a function';
        }
        if (responseArray.length >=2)
        {
            timeToWait = responseArray[1];
            if (typeof(timeToWait) != 'number')
                throw 'Erorr: time to wait for response must be a number';
        }
        if (responseArray.length >= 3)
        {
            noRespFunc = responseArray[2];
            if (typeof(noRespFunc) != 'function')
                throw 'Error: Third arg in response array must be a function';
        }

        if (mrs.oldSeqNo != null)
            mrs.oldSeqNo +=1;

        return std.messaging.sendMessage(mrs.mrp.msg, mrs.mrp.receiver,mrs.sender,respFunc,timeToWait,noRespFunc, mrs.mrp.streamID);
    }

    /**
     lhs >> rhs;

     @lhs {object} message to send

     @rhs {visible} receiver to send message to.

     @return {MessageReceiverPair} Returns a message receiver pair
     object.  This object can later be used to actually send the
     message.  (eg. presToSendFrom : message_receiver_pair >> []; See
     alternate arguments to understand message_receiver_pair >> []; )

     or

     @lhs {MessageReceiverPair} An object that contains both the
     message to send and the receiver that is supposed to receive it.
     In these cases, message will be sent from system.self.

     @rhs {array} A response array.  Can contain up to 3 fields.
     rhs[0] is a function to execute when receive a response.  rhs[1]
     is a number representing the number of seconds to wait before
     stopping listening for a response.  rhs[2] is a function that
     gets executed when we have not received a response within rhs[1]
     seconds.

     @return {ClearObject} @see return type for
     std.messaging.sendMessage.

     or

     @lhs {MessageReceiverSender} An object containing a
     MessageReceiverPair as well as holding

     @rhs (see above rhs)

     @return {ClearObject} Same as above.  @see return type for
     std.messaging.sendMessage.

     */
    std.messaging.sendSyntax = function (lhs, rhs)
    {
        if (checkIsMessageReceiverPair(lhs) && checkIsArray(rhs))
            return callSendMessageReceiverPair(lhs,rhs);
        else if (checkIsMessageReceiverSender(lhs) && checkIsArray(rhs))
            return callSendMessageReceiverSender(lhs,rhs);
        else if ((typeof(lhs) == 'object') && (checkIsVisible(rhs)))
            return new MessageReceiverPair(lhs,rhs);
        //lkjs;

        throw 'Error in sender syntax.  Require either that: 1) lhs must be senderReceiverPair and rhs must contain message handling code; or 2) lhs must be object and rhs must be visible.  Aborting.';
    };

    /**
     Map.  Keys: receiver.toString + '---' + sender.toString + '|||' +
     seqNo.toString(); Values: An array.  First field of array contains the
     handler registered to listen for the response.  The second field contains
     the timeout registered to fire if haven't received a response in a certain
     period of time.
     */
    var openHandlers ={   };

    /**
     @param {string} recString the string address representing the receiver of
     the message that we're sending.
     @param {string} senderString the string address representing the sender of
     the message that we're sending.
     @param {int} seqNo. The sequence number that we're sending the message off
     with.

     @return Returns a key contructed from these three values that can be used
     to access the respHandler and onNoRespTimer stored in openHandlers.
     */
    function generateKey(recString,senderString,seqNo)
    {
        return recString + '---' + senderString + '|||' + seqNo.toString();
    }

    /**
     For description of recString,senderString, and seqNo, @see generateKey
     documentation.

     @param {handler} respHandler Handle to handler registered to fire when
     receive response message.

     @param {timeout} onNoRespTimer.  Handle to timeout registered to fire if
     haven't received a response within the proscribed time.

     @return {ClearObject} Returns an object whose methods can be used to call
     "clear", which aborts listening for the response to the message.

     Registers the key generated from recString,senderString,seqNo with value of
     a two-long array containing handles to the respHandler and onNoRespTimer.
     */
    function addOpenHandler(recString,senderString, seqNo,respHandler,onNoRespTimer)
    {
        var key = generateKey(recString,senderString,seqNo);
        openHandlers[key] = [respHandler,onNoRespTimer];

        return new ClearObject(key);
    }

    /**
     Calling send message returns this object. This object can be used
     to cancel listening for responses to the message that was sent
     out.
     */
    function ClearObject(key)
    {
        var hasCanceled = false;
        this.key = key;
        this.clear = function()
        {
            if (hasCanceled)
                throw 'Error.  Cannot clear a message stream that was already cancelled.';
            cancelOpenHandler(key);
        };

        this.isCleared = function()
        {
            return hasCanceled;
        };
    }

    /**
     @see generateKey for recString,senderString, and seqNo.

     This cancels the onNoRespTimer and respHandler associated with the key
     generated from recString,senderString, and seqNo.  It also removes the key
     from openHandlers.
     */
    function cancelOpenHandler(recString,senderString,seqNo)
    {
        var key = generateKey(recString,senderString,seqNo);
        if (! (key in openHandlers))
            throw 'Error in successResponse.  Do not have key ' + key + ' in openHandlers.';

        //clear success handler
        openHandlers[key][0].clear();
        //clear timeout handler if it isn't null
        if (openHandlers[key][1] != null)
            openHandlers[key][1].clear();

        delete openHandlers[key];
    }

    std.messaging.makeReply = function(oldMsg,sndr)
    {
        var streamID = -1;
        if (typeof(oldMsg.streamID) == 'number')
            streamID = oldMsg.streamID;

        var returner = function(newMsg)
        {
            var mrp = new MessageReceiverPair(newMsg,sndr,streamID);
            return new std.messaging.MessageReceiverSender(system.self,mrp);
        };
        return returner;
    };

    /**
     @param {object} msg.  Message we want to send to receiver from sender
     @param {visible} receiver.  The future recipient of msg and.
     @param {presence} sender.  The sender of msg.
     @param {function} onResp.  A function that executes if the receiver
     responds to the message.  The function takes two parameters: the new
     message received and the sender of that message (receiver).
     @param {float} timeToWait.  The amount of time to wait before
     de-registering the onResp handler and triggering the onNoResp function.
     @param {function} onNoResp.  A function to execute if timeToWait seconds
     have gone by and we haven't received a response to our message.

     @param {number} streamID.  The id for the stream that we are sending

     @return {ClearObject} Returns an object whose methods can be used to call
     "clear", which aborts listening for the response to the message.

     */
    std.messaging.sendMessage = function (msg,receiver,sender, onResp, timeToWait,onNoResp, streamID)
    {
        msg.seqNo =     std.messaging.seqNumManager.getSeqNumber(sender,receiver,streamID);
        msg.streamID = streamID;

        //actually send the message.
        system.sendMessage(sender,msg,receiver);

        var recString = receiver.toString();
        var senderString = sender.toString();
        var seqNo = msg.seqNo;

        system.print('\nDEBUG: sending a message with seqNo: ' + seqNo.toString() + '\n\n');

        var wrapOnResp = function(msgRec,sndr)
        {
            cancelOpenHandler(recString,senderString,seqNo);

            //calling makeReply generates a new MessageReceiverSender
            //object.  Can use this object to send additional
            //messages.
            // msgRec.makeReply = function(newMsg)
            // {
            //     var mrp = new MessageReceiverPair(newMsg,sndr,streamID);
            //     return new std.messaging.MessageReceiverSender(system.self,mrp);
            // };

            onResp(msgRec,sndr);
        };

        var wrapOnNoResp = function()
        {
            cancelOpenHandler(recString,senderString,seqNo);
            onNoResp();
        };

        var respHandler    = system.registerHandler(wrapOnResp,{'seqNo': msg.seqNo+1: }, receiver );
        var onNoRespTimeout = null;
        if (timeToWait != null)
            onNoRespTimeout = system.timeout(timeToWait,wrapOnNoResp);

        return addOpenHandler(recString,senderString,seqNo,respHandler,onNoRespTimeout);

    };

}
)();


install python3 for wily and xenial

install python3 for wily and xenial

@[if os_name == 'ubuntu' and os_code_name in ['saucy', 'vivid', 'wily', 'xenial']]@
@# Ubuntu Saucy, Vivid and newer have neither Python 2 nor 3 installed by default
RUN for i in 1 2 3; do apt-get update && apt-get install -q -y python3 && break || sleep 5; done
@[end if]@

@[if os_name == 'ubuntu' and os_code_name in ['saucy', 'vivid']]@
@# Ubuntu Saucy and Vivid have neither Python 2 nor 3 installed by default
RUN for i in 1 2 3; do apt-get update && apt-get install -q -y python3 && break || sleep 5; done
@[end if]@


fix check for existing description tag (#518)

import difflib.DiffUtils
import groovy.io.FileType
@@ThreadInterrupt
import groovy.transform.ThreadInterrupt
import hudson.AbortException
import hudson.model.View
import java.io.StringBufferInputStream
import java.io.StringWriter
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.transform.stream.StreamSource
import jenkins.model.Jenkins
import org.apache.xml.serialize.OutputFormat
import org.apache.xml.serialize.XMLSerializer
import org.xml.sax.InputSource

// job types with their prefix and a list of job names
// used to remove obsolete jobs with the same prefix which are not part of the list
job_prefixes_and_names = [:]
@[for job_type in sorted(job_prefixes_and_names.keys())]@
job_prefixes_and_names['@job_type'] = [
    'job_prefix': '@(job_prefixes_and_names[job_type][0])',
    'job_names': [],
]
@{
job_names = sorted(job_prefixes_and_names[job_type][1])
group_size = 1000
}@
// group job names in chunks of @group_size to not exceed groovy limits
@[for i in range(0, len(job_names), group_size)]@
def add_job_prefixes_and_names_@(job_type)_@(int(i / group_size) + 1)(job_names) {
@[for j in range(i, min(i + group_size, len(job_names)))]@
job_names << '@(job_names[j])'
@[end for]@
}
add_job_prefixes_and_names_@(job_type)_@(int(i / group_size) + 1)(job_prefixes_and_names['@job_type'].job_names)
@[end for]@
@[end for]@

println '# BEGIN SECTION: Groovy script - reconfigure'
dry_run = @('true' if dry_run else 'false')
dry_run_suffix = dry_run ? ' (dry run)' : ''

@[if vars().get('expected_num_views')]@
// reconfigure views
println '# BEGIN SUBSECTION: reconfigure @(expected_num_views) views'
created_views = 0
updated_views = 0
skipped_views = 0

view_config_dir = build.getWorkspace().toString() + '/reconfigure_jobs/view_configs'

def view_dir = new File(view_config_dir)
def views = view_dir.listFiles()
views.sort()

if (views.size() != @(expected_num_views)) {
    println "ERROR: Found different number of view configs than expected!! " + views.size() + " is not @(expected_num_views) as expected."
    // fail this build
    throw new AbortException("Wrong number of view configs")
}

for (it in views) {
    found_view = false
    view_name = it.getName()
    view_config = new File(it.path).getText('UTF-8')
    for (v in Jenkins.instance.views) {
        if (v.name != view_name) continue
        found_view = true
        output_stream= new ByteArrayOutputStream()
        v.writeXml(output_stream)
        reader = new StringReader(output_stream.toString())
        source = new InputSource(reader)

        diff = diff_configs(source, view_config)
        if (!diff) {
            println "Skipped view '" + view_name + "' because the config is the same" + dry_run_suffix
            skipped_views += 1
        } else {
            println "Updating view '" + view_name + "'" + dry_run_suffix
            println '    <<<'
            for (line in diff) {
                println '    ' + line
            }
            println '    >>>'

            if (!dry_run) {
                reader = new StringReader(view_config)
                source = new StreamSource(reader)
                v.updateByXml(source)
            }
            updated_views += 1
        }
        break
    }
    if (!found_view) {
        println "Creating view '" + view_name + "'" + dry_run_suffix
        if (!dry_run) {
            stream = new StringBufferInputStream(view_config)
            view = View.createViewFromXML(view_name, stream)
            Jenkins.instance.addView(view)
        }
        created_views += 1
    }
}

println 'Created ' + created_views + ' views, updated ' + updated_views + ' views, skipped ' + skipped_views + ' views' + dry_run_suffix + '.'
println '# END SUBSECTION'

@[end if]@
// reconfigure jobs
println '# BEGIN SUBSECTION: reconfigure @(expected_num_jobs) jobs'
created_jobs = 0
updated_jobs = 0
skipped_jobs = 0

job_config_dir = build.getWorkspace().toString() + '/reconfigure_jobs/job_configs'

def job_dir = new File(job_config_dir)
def jobs = job_dir.listFiles()
jobs.sort()

if (jobs.size() != @(expected_num_jobs)) {
    println "ERROR: Found different number of job configs than expected!! " + jobs.size() + " is not @(expected_num_jobs) as expected."
    // fail this build
    throw new AbortException("Wrong number of job configs")
}

def mergePullRequestData(job_name, current_file, job_config) {
    factory = DocumentBuilderFactory.newInstance()
    builder = factory.newDocumentBuilder()
    document = builder.parse(current_file)

    nodelist = document.getElementsByTagName("triggers")
    if (nodelist.getLength() != 1) return job_config
    trigger_node = nodelist.item(0)

    nodelist = trigger_node.getElementsByTagName("org.jenkinsci.plugins.ghprb.GhprbTrigger")
    if (nodelist.getLength() != 1) return job_config
    ghprb_trigger_node = nodelist.item(0)

    nodelist = ghprb_trigger_node.getElementsByTagName("pullRequests")
    if (nodelist.getLength() != 1) return job_config
    pull_requests_node = nodelist.item(0)

    builder2 = factory.newDocumentBuilder()
    stream = new StringBufferInputStream(job_config)
    document2 = builder2.parse(stream)

    nodelist = document2.getElementsByTagName("triggers")
    if (nodelist.getLength() != 1) return job_config
    trigger_node = nodelist.item(0)

    nodelist = trigger_node.getElementsByTagName("org.jenkinsci.plugins.ghprb.GhprbTrigger")
    if (nodelist.getLength() != 1) return job_config
    ghprb_trigger_node = nodelist.item(0)

    pull_requests_node = document2.importNode(pull_requests_node, true)
    ghprb_trigger_node.appendChild(pull_requests_node)

    println "Merged configuration with existing pull request job '" + job_name + "' to maintain pull request information"

    return format_xml(document2)
}

for (it in jobs) {
    job_name = it.getName()
    // remove leading serial number
    job_name = job_name[job_name.indexOf(' ') + 1..-1]
    job_config = new File(it.path).getText('UTF-8')
    p = Jenkins.instance.getItemByFullName(job_name)
    if (p) {
        job_config_file = p.getConfigFile()

        if (p.name.substring(1, 5) == 'pr__') {
            job_config = mergePullRequestData(job_name, job_config_file.getFile(), job_config)
        }

        diff = diff_configs(job_config_file.getFile(), job_config)
        if (!diff) {
            println "Skipped job '" + job_name + "' [" + (skipped_jobs + updated_jobs + created_jobs + 1) + " / " + jobs.length + "] because the config is the same" + dry_run_suffix
            skipped_jobs += 1
        } else {
            println "Updating job '" + job_name + "' [" + (skipped_jobs + updated_jobs + created_jobs + 1) + " / " + jobs.length + "]" + dry_run_suffix
            println '    <<<'
            for (line in diff) {
                println '    ' + line
            }
            println '    >>>'

            if (!dry_run) {
                reader = new StringReader(job_config)
                source = new StreamSource(reader)
                p.updateByXml(source)
            }
            updated_jobs += 1
        }
    } else {
        println "Creating job '" + job_name + "' [" + (skipped_jobs + updated_jobs + created_jobs + 1) + " / " + jobs.length + "]" + dry_run_suffix
        if (!dry_run) {
            stream = new StringBufferInputStream(job_config)
            Jenkins.instance.createProjectFromXML(job_name, stream)
        }
        created_jobs += 1
    }
}

println 'Created ' + created_jobs + ' jobs, updated ' + updated_jobs + ' jobs, skipped ' + skipped_jobs + ' jobs' + dry_run_suffix + '.'
println 'Rebuilding dependency graph...'
Jenkins.instance.rebuildDependencyGraph()
println '# END SUBSECTION'

// delete obsolete jobs
for (item in job_prefixes_and_names) {
    job_type = item.key
    job_prefix = item.value.job_prefix
    job_names = item.value.job_names
    deleted = 0
    println "# BEGIN SUBSECTION: Groovy script - delete obsolete '" + job_type + "' jobs"
    println "Searching for obsolete jobs starting with '" + job_prefix + "'"
    for (p in Jenkins.instance.allItems) {
        if (!p.name.startsWith(job_prefix)) continue
        if (p.name in job_names) continue
        println "Deleting job '" + p.name + "'" + dry_run_suffix
        if (!dry_run) {
            p.delete()
        }
        deleted += 1
    }
    println 'Deleted ' + deleted + ' jobs' + dry_run_suffix + '.'
    println '# END SUBSECTION'
}

println '# END SECTION'

def diff_configs(current_config, new_config) {

    factory = DocumentBuilderFactory.newInstance()
    builder = factory.newDocumentBuilder()

    current_doc = builder.parse(current_config)

    new_config_stream = new StringBufferInputStream(new_config)
    new_doc = builder.parse(new_config_stream)

    // ignore description which contains a timestamp
    if (current_doc.getElementsByTagName('description').getLength() > 0) {
        current_doc.getElementsByTagName('description').item(0).setTextContent('')
    }
    if (new_doc.getElementsByTagName('description').getLength() > 0) {
        new_doc.getElementsByTagName('description').item(0).setTextContent('')
    }

    current_string = format_xml(current_doc)
    new_string = format_xml(new_doc)

    if (current_string == new_string) {
        return []
    }

    current_lines = []
    current_string.eachLine { line, count ->
        current_lines << line
    }
    new_lines = []
    new_string.eachLine { line, count ->
        new_lines << line
    }

    patch = DiffUtils.diff(current_lines, new_lines)
    return DiffUtils.generateUnifiedDiff('current config', 'new config', current_lines, patch, 0)
}

def format_xml(doc) {
    format = new OutputFormat(doc)
    format.setIndenting(true)
    format.setIndent(2)
    writer = new StringWriter()
    serializer = new XMLSerializer(writer, format)
    serializer.serialize(doc)
    return writer.toString()
}

import difflib.DiffUtils
import groovy.io.FileType
@@ThreadInterrupt
import groovy.transform.ThreadInterrupt
import hudson.AbortException
import hudson.model.View
import java.io.StringBufferInputStream
import java.io.StringWriter
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.transform.stream.StreamSource
import jenkins.model.Jenkins
import org.apache.xml.serialize.OutputFormat
import org.apache.xml.serialize.XMLSerializer
import org.xml.sax.InputSource

// job types with their prefix and a list of job names
// used to remove obsolete jobs with the same prefix which are not part of the list
job_prefixes_and_names = [:]
@[for job_type in sorted(job_prefixes_and_names.keys())]@
job_prefixes_and_names['@job_type'] = [
    'job_prefix': '@(job_prefixes_and_names[job_type][0])',
    'job_names': [],
]
@{
job_names = sorted(job_prefixes_and_names[job_type][1])
group_size = 1000
}@
// group job names in chunks of @group_size to not exceed groovy limits
@[for i in range(0, len(job_names), group_size)]@
def add_job_prefixes_and_names_@(job_type)_@(int(i / group_size) + 1)(job_names) {
@[for j in range(i, min(i + group_size, len(job_names)))]@
job_names << '@(job_names[j])'
@[end for]@
}
add_job_prefixes_and_names_@(job_type)_@(int(i / group_size) + 1)(job_prefixes_and_names['@job_type'].job_names)
@[end for]@
@[end for]@

println '# BEGIN SECTION: Groovy script - reconfigure'
dry_run = @('true' if dry_run else 'false')
dry_run_suffix = dry_run ? ' (dry run)' : ''

@[if vars().get('expected_num_views')]@
// reconfigure views
println '# BEGIN SUBSECTION: reconfigure @(expected_num_views) views'
created_views = 0
updated_views = 0
skipped_views = 0

view_config_dir = build.getWorkspace().toString() + '/reconfigure_jobs/view_configs'

def view_dir = new File(view_config_dir)
def views = view_dir.listFiles()
views.sort()

if (views.size() != @(expected_num_views)) {
    println "ERROR: Found different number of view configs than expected!! " + views.size() + " is not @(expected_num_views) as expected."
    // fail this build
    throw new AbortException("Wrong number of view configs")
}

for (it in views) {
    found_view = false
    view_name = it.getName()
    view_config = new File(it.path).getText('UTF-8')
    for (v in Jenkins.instance.views) {
        if (v.name != view_name) continue
        found_view = true
        output_stream= new ByteArrayOutputStream()
        v.writeXml(output_stream)
        reader = new StringReader(output_stream.toString())
        source = new InputSource(reader)

        diff = diff_configs(source, view_config)
        if (!diff) {
            println "Skipped view '" + view_name + "' because the config is the same" + dry_run_suffix
            skipped_views += 1
        } else {
            println "Updating view '" + view_name + "'" + dry_run_suffix
            println '    <<<'
            for (line in diff) {
                println '    ' + line
            }
            println '    >>>'

            if (!dry_run) {
                reader = new StringReader(view_config)
                source = new StreamSource(reader)
                v.updateByXml(source)
            }
            updated_views += 1
        }
        break
    }
    if (!found_view) {
        println "Creating view '" + view_name + "'" + dry_run_suffix
        if (!dry_run) {
            stream = new StringBufferInputStream(view_config)
            view = View.createViewFromXML(view_name, stream)
            Jenkins.instance.addView(view)
        }
        created_views += 1
    }
}

println 'Created ' + created_views + ' views, updated ' + updated_views + ' views, skipped ' + skipped_views + ' views' + dry_run_suffix + '.'
println '# END SUBSECTION'

@[end if]@
// reconfigure jobs
println '# BEGIN SUBSECTION: reconfigure @(expected_num_jobs) jobs'
created_jobs = 0
updated_jobs = 0
skipped_jobs = 0

job_config_dir = build.getWorkspace().toString() + '/reconfigure_jobs/job_configs'

def job_dir = new File(job_config_dir)
def jobs = job_dir.listFiles()
jobs.sort()

if (jobs.size() != @(expected_num_jobs)) {
    println "ERROR: Found different number of job configs than expected!! " + jobs.size() + " is not @(expected_num_jobs) as expected."
    // fail this build
    throw new AbortException("Wrong number of job configs")
}

def mergePullRequestData(job_name, current_file, job_config) {
    factory = DocumentBuilderFactory.newInstance()
    builder = factory.newDocumentBuilder()
    document = builder.parse(current_file)

    nodelist = document.getElementsByTagName("triggers")
    if (nodelist.getLength() != 1) return job_config
    trigger_node = nodelist.item(0)

    nodelist = trigger_node.getElementsByTagName("org.jenkinsci.plugins.ghprb.GhprbTrigger")
    if (nodelist.getLength() != 1) return job_config
    ghprb_trigger_node = nodelist.item(0)

    nodelist = ghprb_trigger_node.getElementsByTagName("pullRequests")
    if (nodelist.getLength() != 1) return job_config
    pull_requests_node = nodelist.item(0)

    builder2 = factory.newDocumentBuilder()
    stream = new StringBufferInputStream(job_config)
    document2 = builder2.parse(stream)

    nodelist = document2.getElementsByTagName("triggers")
    if (nodelist.getLength() != 1) return job_config
    trigger_node = nodelist.item(0)

    nodelist = trigger_node.getElementsByTagName("org.jenkinsci.plugins.ghprb.GhprbTrigger")
    if (nodelist.getLength() != 1) return job_config
    ghprb_trigger_node = nodelist.item(0)

    pull_requests_node = document2.importNode(pull_requests_node, true)
    ghprb_trigger_node.appendChild(pull_requests_node)

    println "Merged configuration with existing pull request job '" + job_name + "' to maintain pull request information"

    return format_xml(document2)
}

for (it in jobs) {
    job_name = it.getName()
    // remove leading serial number
    job_name = job_name[job_name.indexOf(' ') + 1..-1]
    job_config = new File(it.path).getText('UTF-8')
    p = Jenkins.instance.getItemByFullName(job_name)
    if (p) {
        job_config_file = p.getConfigFile()

        if (p.name.substring(1, 5) == 'pr__') {
            job_config = mergePullRequestData(job_name, job_config_file.getFile(), job_config)
        }

        diff = diff_configs(job_config_file.getFile(), job_config)
        if (!diff) {
            println "Skipped job '" + job_name + "' [" + (skipped_jobs + updated_jobs + created_jobs + 1) + " / " + jobs.length + "] because the config is the same" + dry_run_suffix
            skipped_jobs += 1
        } else {
            println "Updating job '" + job_name + "' [" + (skipped_jobs + updated_jobs + created_jobs + 1) + " / " + jobs.length + "]" + dry_run_suffix
            println '    <<<'
            for (line in diff) {
                println '    ' + line
            }
            println '    >>>'

            if (!dry_run) {
                reader = new StringReader(job_config)
                source = new StreamSource(reader)
                p.updateByXml(source)
            }
            updated_jobs += 1
        }
    } else {
        println "Creating job '" + job_name + "' [" + (skipped_jobs + updated_jobs + created_jobs + 1) + " / " + jobs.length + "]" + dry_run_suffix
        if (!dry_run) {
            stream = new StringBufferInputStream(job_config)
            Jenkins.instance.createProjectFromXML(job_name, stream)
        }
        created_jobs += 1
    }
}

println 'Created ' + created_jobs + ' jobs, updated ' + updated_jobs + ' jobs, skipped ' + skipped_jobs + ' jobs' + dry_run_suffix + '.'
println 'Rebuilding dependency graph...'
Jenkins.instance.rebuildDependencyGraph()
println '# END SUBSECTION'

// delete obsolete jobs
for (item in job_prefixes_and_names) {
    job_type = item.key
    job_prefix = item.value.job_prefix
    job_names = item.value.job_names
    deleted = 0
    println "# BEGIN SUBSECTION: Groovy script - delete obsolete '" + job_type + "' jobs"
    println "Searching for obsolete jobs starting with '" + job_prefix + "'"
    for (p in Jenkins.instance.allItems) {
        if (!p.name.startsWith(job_prefix)) continue
        if (p.name in job_names) continue
        println "Deleting job '" + p.name + "'" + dry_run_suffix
        if (!dry_run) {
            p.delete()
        }
        deleted += 1
    }
    println 'Deleted ' + deleted + ' jobs' + dry_run_suffix + '.'
    println '# END SUBSECTION'
}

println '# END SECTION'

def diff_configs(current_config, new_config) {

    factory = DocumentBuilderFactory.newInstance()
    builder = factory.newDocumentBuilder()

    current_doc = builder.parse(current_config)

    new_config_stream = new StringBufferInputStream(new_config)
    new_doc = builder.parse(new_config_stream)

    // ignore description which contains a timestamp
    if (current_doc.getElementsByTagName('description')) {
        current_doc.getElementsByTagName('description').item(0).setTextContent('')
    }
    if (new_doc.getElementsByTagName('description')) {
        new_doc.getElementsByTagName('description').item(0).setTextContent('')
    }

    current_string = format_xml(current_doc)
    new_string = format_xml(new_doc)

    if (current_string == new_string) {
        return []
    }

    current_lines = []
    current_string.eachLine { line, count ->
        current_lines << line
    }
    new_lines = []
    new_string.eachLine { line, count ->
        new_lines << line
    }

    patch = DiffUtils.diff(current_lines, new_lines)
    return DiffUtils.generateUnifiedDiff('current config', 'new config', current_lines, patch, 0)
}

def format_xml(doc) {
    format = new OutputFormat(doc)
    format.setIndenting(true)
    format.setIndent(2)
    writer = new StringWriter()
    serializer = new XMLSerializer(writer, format)
    serializer.serialize(doc)
    return writer.toString()
}


Rotate ROS repository keys.

Rotate ROS repository keys.

@(TEMPLATE(
    'snippet/add_generated_comment.Dockerfile.em',
    user_name=user_name,
    tag_name=tag_name,
    source_template_name=template_name,
))@
@(TEMPLATE(
    'snippet/from_base_image.Dockerfile.em',
    template_packages=template_packages,
    os_name=os_name,
    os_code_name=os_code_name,
    arch=arch,
    base_image=base_image,
    maintainer_name=maintainer_name,
))@
@
@(TEMPLATE(
    'snippet/setup_tzdata.Dockerfile.em',
    os_name=os_name,
    os_code_name=os_code_name,
))@
@
@{
template_dependencies = [
    'dirmngr',
    'gnupg2',
    'lsb-release',
]
}@
@(TEMPLATE(
    'snippet/install_upstream_package_list.Dockerfile.em',
    packages=template_dependencies,
    upstream_packages=upstream_packages if 'upstream_packages' in locals() else [],
))@
@
# setup keys
RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654

# setup sources.list
RUN echo "deb http://packages.ros.org/ros/ubuntu `lsb_release -sc` main" > /etc/apt/sources.list.d/ros-latest.list

@(TEMPLATE(
    'snippet/install_ros_bootstrap_tools.Dockerfile.em',
    ros_version=ros_version,
))@

# setup environment
ENV LANG C.UTF-8
ENV LC_ALL C.UTF-8

# bootstrap rosdep
RUN rosdep init \
    && rosdep update

# install ros packages
ENV ROS_DISTRO @rosdistro_name
RUN apt-get update && apt-get install -y \
    @(' \\\n    '.join(ros_packages))@  \
    && rm -rf /var/lib/apt/lists/*

@[if 'entrypoint_name' in locals()]@
@[  if entrypoint_name]@
@{
entrypoint_file = entrypoint_name.split('/')[-1]
}@
# setup entrypoint
COPY ./@entrypoint_file /

ENTRYPOINT ["/@entrypoint_file"]
@[  end if]@
@[end if]@
@{
cmds = [
'bash',
]
}@
CMD ["@(' && '.join(cmds))"]

@(TEMPLATE(
    'snippet/add_generated_comment.Dockerfile.em',
    user_name=user_name,
    tag_name=tag_name,
    source_template_name=template_name,
))@
@(TEMPLATE(
    'snippet/from_base_image.Dockerfile.em',
    template_packages=template_packages,
    os_name=os_name,
    os_code_name=os_code_name,
    arch=arch,
    base_image=base_image,
    maintainer_name=maintainer_name,
))@
@
@(TEMPLATE(
    'snippet/setup_tzdata.Dockerfile.em',
    os_name=os_name,
    os_code_name=os_code_name,
))@
@
@{
template_dependencies = [
    'dirmngr',
    'gnupg2',
    'lsb-release',
]
}@
@(TEMPLATE(
    'snippet/install_upstream_package_list.Dockerfile.em',
    packages=template_dependencies,
    upstream_packages=upstream_packages if 'upstream_packages' in locals() else [],
))@
@
# setup keys
RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 421C365BD9FF1F717815A3895523BAEEB01FA116

# setup sources.list
RUN echo "deb http://packages.ros.org/ros/ubuntu `lsb_release -sc` main" > /etc/apt/sources.list.d/ros-latest.list

@(TEMPLATE(
    'snippet/install_ros_bootstrap_tools.Dockerfile.em',
    ros_version=ros_version,
))@

# setup environment
ENV LANG C.UTF-8
ENV LC_ALL C.UTF-8

# bootstrap rosdep
RUN rosdep init \
    && rosdep update

# install ros packages
ENV ROS_DISTRO @rosdistro_name
RUN apt-get update && apt-get install -y \
    @(' \\\n    '.join(ros_packages))@  \
    && rm -rf /var/lib/apt/lists/*

@[if 'entrypoint_name' in locals()]@
@[  if entrypoint_name]@
@{
entrypoint_file = entrypoint_name.split('/')[-1]
}@
# setup entrypoint
COPY ./@entrypoint_file /

ENTRYPOINT ["/@entrypoint_file"]
@[  end if]@
@[end if]@
@{
cmds = [
'bash',
]
}@
CMD ["@(' && '.join(cmds))"]


ensure that folder can be removed

ensure that folder can be removed

<project>
  <actions/>
  <description>Generated at @ESCAPE(now_str) from template '@ESCAPE(template_name)'
@[if disabled]@
  but disabled since the package is blacklisted (or not whitelisted) in the configuration file@
@[end if]@
@ </description>
@(SNIPPET(
    'log-rotator',
    days_to_keep=180,
    num_to_keep=30,
))@
  <keepDependencies>false</keepDependencies>
  <properties>
@(SNIPPET(
    'property_github-project',
    project_url=github_url,
))@
@[if job_priority is not None]@
@(SNIPPET(
    'property_job-priority',
    priority=job_priority,
))@
@[end if]@
@(SNIPPET(
    'property_parameters-definition',
    parameters=[
        {
            'type': 'boolean',
            'name': 'skip_cleanup',
            'description': 'Skip cleanup of build artifacts',
        },
    ],
))@
@(SNIPPET(
    'property_requeue-job',
))@
  </properties>
@(SNIPPET(
    'scm_null',
))@
  <assignedNode>@(node_label if node_label else 'buildslave')</assignedNode>
  <canRoam>false</canRoam>
  <disabled>@('true' if disabled else 'false')</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>true</blockBuildWhenUpstreamBuilding>
  <triggers>
@[if upstream_projects]@
@(SNIPPET(
    'trigger_reverse-build',
    upstream_projects=upstream_projects,
))@
@[end if]@
  </triggers>
  <concurrentBuild>false</concurrentBuild>
  <builders>
@(SNIPPET(
    'builder_system-groovy_verify-upstream',
))@
@(SNIPPET(
    'builder_system-groovy_check-free-disk-space',
))@
@(SNIPPET(
    'builder_shell_docker-info',
))@
@(SNIPPET(
    'builder_check-docker',
))@
@(SNIPPET(
    'builder_shell_clone-ros-buildfarm',
    ros_buildfarm_repository=ros_buildfarm_repository,
))@
@(SNIPPET(
    'builder_shell_key-files',
    script_generating_key_files=script_generating_key_files,
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        'rm -fr $WORKSPACE/docker_generating_docker',
        'mkdir -p $WORKSPACE/docker_generating_docker',
        '',
        '# monitor all subprocesses and enforce termination',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/subprocess_reaper.py $$ --cid-file $WORKSPACE/docker_generating_docker/docker.cid > $WORKSPACE/docker_generating_docker/subprocess_reaper.log 2>&1 &',
        '# sleep to give python time to startup',
        'sleep 1',
        '',
        '# generate Dockerfile, build and run it',
        '# generating the Dockerfile for the actual binarydeb task',
        'echo "# BEGIN SECTION: Generate Dockerfile - binarydeb task"',
        'export TZ="%s"' % timezone,
        'export PYTHONPATH=$WORKSPACE/ros_buildfarm:$PYTHONPATH',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/release/run_binarydeb_job.py' +
        ' --rosdistro-index-url ' + rosdistro_index_url +
        ' ' + rosdistro_name +
        ' ' + pkg_name +
        ' ' + os_name +
        ' ' + os_code_name +
        ' ' + arch +
        ' ' + ' '.join(repository_args) +
        ' --binarydeb-dir $WORKSPACE/binarydeb' +
        ' --dockerfile-dir $WORKSPACE/docker_generating_docker' +
        (' --append-timestamp' if append_timestamp else ''),
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Build Dockerfile - binarydeb task"',
        'cd $WORKSPACE/docker_generating_docker',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/misc/docker_pull_baseimage.py',
        'docker build --force-rm -t binarydeb_task_generation.%s_%s_%s_%s_%s .' % (rosdistro_name, os_name, os_code_name, arch, pkg_name),
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Run Dockerfile - binarydeb task"',
        '# ensure to have write permission before trying to delete the folder',
        'if [ -f $WORKSPACE/binarydeb ] ; then chmod -R u+w $WORKSPACE/binarydeb ; fi',
        'rm -fr $WORKSPACE/binarydeb',
        'rm -fr $WORKSPACE/docker_build_binarydeb',
        'mkdir -p $WORKSPACE/binarydeb',
        'mkdir -p $WORKSPACE/docker_build_binarydeb',
        'docker run' +
        ' --rm ' +
        ' --cidfile=$WORKSPACE/docker_generating_docker/docker.cid' +
        ' -v $WORKSPACE/ros_buildfarm:/tmp/ros_buildfarm:ro' +
        ' -v $WORKSPACE/binarydeb:/tmp/binarydeb' +
        ' -v $WORKSPACE/docker_build_binarydeb:/tmp/docker_build_binarydeb' +
        ' -v ~/.ccache:/home/buildfarm/.ccache' + \
        ' binarydeb_task_generation.%s_%s_%s_%s_%s' % (rosdistro_name, os_name, os_code_name, arch, pkg_name),
        'echo "# END SECTION"',
    ]),
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        '# monitor all subprocesses and enforce termination',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/subprocess_reaper.py $$ --cid-file $WORKSPACE/docker_build_binarydeb/docker.cid > $WORKSPACE/docker_build_binarydeb/subprocess_reaper.log 2>&1 &',
        '# sleep to give python time to startup',
        'sleep 1',
        '',
        'echo "# BEGIN SECTION: Build Dockerfile - build binarydeb"',
        '# build and run build_binarydeb Dockerfile',
        'cd $WORKSPACE/docker_build_binarydeb',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/misc/docker_pull_baseimage.py',
        'docker build --force-rm -t binarydeb_build.%s_%s_%s_%s_%s .' % (rosdistro_name, os_name, os_code_name, arch, pkg_name),
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Run Dockerfile - build binarydeb"',
        '# -e=HOME= is required to set a reasonable HOME for the user (not /)',
        '# otherwise apt-src will fail',
        'docker run' +
        ' --rm ' +
        ' --cidfile=$WORKSPACE/docker_build_binarydeb/docker.cid' +
        ' -e=HOME=' +
        ' --net=host' +
        ' -v $WORKSPACE/ros_buildfarm:/tmp/ros_buildfarm:ro' +
        ' -v $WORKSPACE/binarydeb:/tmp/binarydeb' +
        ' -v ~/.ccache:/home/buildfarm/.ccache' +
        ' binarydeb_build.%s_%s_%s_%s_%s' % (rosdistro_name, os_name, os_code_name, arch, pkg_name),
        'echo "# END SECTION"',
    ]),
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        'if [ "$skip_cleanup" = "false" ]; then',
        'echo "# BEGIN SECTION: Clean up to save disk space on slaves"',
        '# ensure to have write permission before trying to delete the folder',
        'chmod -R u+w $WORKSPACE/binarydeb',
        'rm -fr binarydeb/*/*',
        'echo "# END SECTION"',
        'fi',
    ]),
))@
@# disable installation check due to huge performance hit
@# @(SNIPPET(
@#     'builder_shell',
@#     script='\n'.join([
@#         'rm -fr $WORKSPACE/docker_install_binarydeb',
@#         'mkdir -p $WORKSPACE/docker_install_binarydeb',
@#         '',
@#         '# monitor all subprocesses and enforce termination',
@#         'python3 -u $WORKSPACE/ros_buildfarm/scripts/subprocess_reaper.py $$ --cid-file $WORKSPACE/docker_install_binarydeb/docker.cid > $WORKSPACE/docker_install_binarydeb/subprocess_reaper.log 2>&1 &',
@#         '# sleep to give python time to startup',
@#         'sleep 1',
@#         '',
@#         '# generate Dockerfile, build and run it',
@#         '# trying to install the built binarydeb',
@#         'echo "# BEGIN SECTION: Generate Dockerfile - install"',
@#         'export TZ="%s"' % timezone,
@#         'export PYTHONPATH=$WORKSPACE/ros_buildfarm:$PYTHONPATH',
@#         'python3 -u $WORKSPACE/ros_buildfarm/scripts/release/create_binarydeb_install_task_generator.py' +
@#         ' ' + os_name +
@#         ' ' + os_code_name +
@#         ' ' + arch +
@#         ' ' + ' '.join(repository_args) +
@#         ' --binarydeb-dir $WORKSPACE/binarydeb' +
@#         ' --dockerfile-dir $WORKSPACE/docker_install_binarydeb',
@#         'echo "# END SECTION"',
@#         '',
@#         'echo "# BEGIN SECTION: Build Dockerfile - install"',
@#         'cd $WORKSPACE/docker_install_binarydeb',
@#         'python3 -u $WORKSPACE/ros_buildfarm/scripts/misc/docker_pull_baseimage.py',
@#         'docker build --force-rm -t binarydeb_install.%s_%s_%s_%s_%s .' % (rosdistro_name, os_name, os_code_name, arch, pkg_name),
@#         'echo "# END SECTION"',
@#         '',
@#         'echo "# BEGIN SECTION: Run Dockerfile - install"',
@#         'docker run' +
@#         ' --rm ' +
@#         ' --cidfile=$WORKSPACE/docker_install_binarydeb/docker.cid' +
@#         ' -v $WORKSPACE/binarydeb:/tmp/binarydeb:ro' +
@#         ' binarydeb_install.%s_%s_%s_%s_%s' % (rosdistro_name, os_name, os_code_name, arch, pkg_name),
@#         'echo "# END SECTION"',
@#     ]),
@# ))@
@(SNIPPET(
    'builder_publish-over-ssh',
    config_name='repo',
    remote_directory='%s/${JOB_NAME}__${BUILD_NUMBER}' % os_code_name,
    source_files=binarydeb_files,
    remove_prefix='binarydeb',
))@
@(SNIPPET(
    'builder_parameterized-trigger',
    project=import_package_job_name,
    parameters='\n'.join([
        'subfolder=%s/${JOB_NAME}__${BUILD_NUMBER}' % os_code_name,
        'debian_package_name=%s' % debian_package_name]),
    continue_on_failure=False,
))@
  </builders>
  <publishers>
@(SNIPPET(
    'publisher_build-trigger',
    child_projects=child_projects,
))@
@(SNIPPET(
    'publisher_description-setter',
    regexp="Package '[^']+' version: (\S+)",
    # to prevent overwriting the description of failed builds
    regexp_for_failed='ThisRegExpShouldNeverMatch',
))@
@[if notify_maintainers]@
@(SNIPPET(
    'publisher_groovy-postbuild_maintainer-notification',
))@
@[end if]@
@(SNIPPET(
    'publisher_mailer',
    recipients=notify_emails,
    dynamic_recipients=maintainer_emails,
    send_to_individuals=False,
))@
  </publishers>
  <buildWrappers>
@[if timeout_minutes is not None]@
@(SNIPPET(
    'build-wrapper_build-timeout',
    timeout_minutes=timeout_minutes,
))@
@[end if]@
@(SNIPPET(
    'build-wrapper_timestamper',
))@
@(SNIPPET(
    'build-wrapper_ssh-agent',
    credential_id=credential_id,
))@
  </buildWrappers>
</project>

<project>
  <actions/>
  <description>Generated at @ESCAPE(now_str) from template '@ESCAPE(template_name)'
@[if disabled]@
  but disabled since the package is blacklisted (or not whitelisted) in the configuration file@
@[end if]@
@ </description>
@(SNIPPET(
    'log-rotator',
    days_to_keep=180,
    num_to_keep=30,
))@
  <keepDependencies>false</keepDependencies>
  <properties>
@(SNIPPET(
    'property_github-project',
    project_url=github_url,
))@
@[if job_priority is not None]@
@(SNIPPET(
    'property_job-priority',
    priority=job_priority,
))@
@[end if]@
@(SNIPPET(
    'property_parameters-definition',
    parameters=[
        {
            'type': 'boolean',
            'name': 'skip_cleanup',
            'description': 'Skip cleanup of build artifacts',
        },
    ],
))@
@(SNIPPET(
    'property_requeue-job',
))@
  </properties>
@(SNIPPET(
    'scm_null',
))@
  <assignedNode>@(node_label if node_label else 'buildslave')</assignedNode>
  <canRoam>false</canRoam>
  <disabled>@('true' if disabled else 'false')</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>true</blockBuildWhenUpstreamBuilding>
  <triggers>
@[if upstream_projects]@
@(SNIPPET(
    'trigger_reverse-build',
    upstream_projects=upstream_projects,
))@
@[end if]@
  </triggers>
  <concurrentBuild>false</concurrentBuild>
  <builders>
@(SNIPPET(
    'builder_system-groovy_verify-upstream',
))@
@(SNIPPET(
    'builder_system-groovy_check-free-disk-space',
))@
@(SNIPPET(
    'builder_shell_docker-info',
))@
@(SNIPPET(
    'builder_check-docker',
))@
@(SNIPPET(
    'builder_shell_clone-ros-buildfarm',
    ros_buildfarm_repository=ros_buildfarm_repository,
))@
@(SNIPPET(
    'builder_shell_key-files',
    script_generating_key_files=script_generating_key_files,
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        'rm -fr $WORKSPACE/docker_generating_docker',
        'mkdir -p $WORKSPACE/docker_generating_docker',
        '',
        '# monitor all subprocesses and enforce termination',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/subprocess_reaper.py $$ --cid-file $WORKSPACE/docker_generating_docker/docker.cid > $WORKSPACE/docker_generating_docker/subprocess_reaper.log 2>&1 &',
        '# sleep to give python time to startup',
        'sleep 1',
        '',
        '# generate Dockerfile, build and run it',
        '# generating the Dockerfile for the actual binarydeb task',
        'echo "# BEGIN SECTION: Generate Dockerfile - binarydeb task"',
        'export TZ="%s"' % timezone,
        'export PYTHONPATH=$WORKSPACE/ros_buildfarm:$PYTHONPATH',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/release/run_binarydeb_job.py' +
        ' --rosdistro-index-url ' + rosdistro_index_url +
        ' ' + rosdistro_name +
        ' ' + pkg_name +
        ' ' + os_name +
        ' ' + os_code_name +
        ' ' + arch +
        ' ' + ' '.join(repository_args) +
        ' --binarydeb-dir $WORKSPACE/binarydeb' +
        ' --dockerfile-dir $WORKSPACE/docker_generating_docker' +
        (' --append-timestamp' if append_timestamp else ''),
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Build Dockerfile - binarydeb task"',
        'cd $WORKSPACE/docker_generating_docker',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/misc/docker_pull_baseimage.py',
        'docker build --force-rm -t binarydeb_task_generation.%s_%s_%s_%s_%s .' % (rosdistro_name, os_name, os_code_name, arch, pkg_name),
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Run Dockerfile - binarydeb task"',
        'rm -fr $WORKSPACE/binarydeb',
        'rm -fr $WORKSPACE/docker_build_binarydeb',
        'mkdir -p $WORKSPACE/binarydeb',
        'mkdir -p $WORKSPACE/docker_build_binarydeb',
        'docker run' +
        ' --rm ' +
        ' --cidfile=$WORKSPACE/docker_generating_docker/docker.cid' +
        ' -v $WORKSPACE/ros_buildfarm:/tmp/ros_buildfarm:ro' +
        ' -v $WORKSPACE/binarydeb:/tmp/binarydeb' +
        ' -v $WORKSPACE/docker_build_binarydeb:/tmp/docker_build_binarydeb' +
        ' -v ~/.ccache:/home/buildfarm/.ccache' + \
        ' binarydeb_task_generation.%s_%s_%s_%s_%s' % (rosdistro_name, os_name, os_code_name, arch, pkg_name),
        'echo "# END SECTION"',
    ]),
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        '# monitor all subprocesses and enforce termination',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/subprocess_reaper.py $$ --cid-file $WORKSPACE/docker_build_binarydeb/docker.cid > $WORKSPACE/docker_build_binarydeb/subprocess_reaper.log 2>&1 &',
        '# sleep to give python time to startup',
        'sleep 1',
        '',
        'echo "# BEGIN SECTION: Build Dockerfile - build binarydeb"',
        '# build and run build_binarydeb Dockerfile',
        'cd $WORKSPACE/docker_build_binarydeb',
        'python3 -u $WORKSPACE/ros_buildfarm/scripts/misc/docker_pull_baseimage.py',
        'docker build --force-rm -t binarydeb_build.%s_%s_%s_%s_%s .' % (rosdistro_name, os_name, os_code_name, arch, pkg_name),
        'echo "# END SECTION"',
        '',
        'echo "# BEGIN SECTION: Run Dockerfile - build binarydeb"',
        '# -e=HOME= is required to set a reasonable HOME for the user (not /)',
        '# otherwise apt-src will fail',
        'docker run' +
        ' --rm ' +
        ' --cidfile=$WORKSPACE/docker_build_binarydeb/docker.cid' +
        ' -e=HOME=' +
        ' --net=host' +
        ' -v $WORKSPACE/ros_buildfarm:/tmp/ros_buildfarm:ro' +
        ' -v $WORKSPACE/binarydeb:/tmp/binarydeb' +
        ' -v ~/.ccache:/home/buildfarm/.ccache' +
        ' binarydeb_build.%s_%s_%s_%s_%s' % (rosdistro_name, os_name, os_code_name, arch, pkg_name),
        'echo "# END SECTION"',
    ]),
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        'if [ "$skip_cleanup" = "false" ]; then',
        'echo "# BEGIN SECTION: Clean up to save disk space on slaves"',
        'rm -fr binarydeb/*/*',
        'echo "# END SECTION"',
        'fi',
    ]),
))@
@# disable installation check due to huge performance hit
@# @(SNIPPET(
@#     'builder_shell',
@#     script='\n'.join([
@#         'rm -fr $WORKSPACE/docker_install_binarydeb',
@#         'mkdir -p $WORKSPACE/docker_install_binarydeb',
@#         '',
@#         '# monitor all subprocesses and enforce termination',
@#         'python3 -u $WORKSPACE/ros_buildfarm/scripts/subprocess_reaper.py $$ --cid-file $WORKSPACE/docker_install_binarydeb/docker.cid > $WORKSPACE/docker_install_binarydeb/subprocess_reaper.log 2>&1 &',
@#         '# sleep to give python time to startup',
@#         'sleep 1',
@#         '',
@#         '# generate Dockerfile, build and run it',
@#         '# trying to install the built binarydeb',
@#         'echo "# BEGIN SECTION: Generate Dockerfile - install"',
@#         'export TZ="%s"' % timezone,
@#         'export PYTHONPATH=$WORKSPACE/ros_buildfarm:$PYTHONPATH',
@#         'python3 -u $WORKSPACE/ros_buildfarm/scripts/release/create_binarydeb_install_task_generator.py' +
@#         ' ' + os_name +
@#         ' ' + os_code_name +
@#         ' ' + arch +
@#         ' ' + ' '.join(repository_args) +
@#         ' --binarydeb-dir $WORKSPACE/binarydeb' +
@#         ' --dockerfile-dir $WORKSPACE/docker_install_binarydeb',
@#         'echo "# END SECTION"',
@#         '',
@#         'echo "# BEGIN SECTION: Build Dockerfile - install"',
@#         'cd $WORKSPACE/docker_install_binarydeb',
@#         'python3 -u $WORKSPACE/ros_buildfarm/scripts/misc/docker_pull_baseimage.py',
@#         'docker build --force-rm -t binarydeb_install.%s_%s_%s_%s_%s .' % (rosdistro_name, os_name, os_code_name, arch, pkg_name),
@#         'echo "# END SECTION"',
@#         '',
@#         'echo "# BEGIN SECTION: Run Dockerfile - install"',
@#         'docker run' +
@#         ' --rm ' +
@#         ' --cidfile=$WORKSPACE/docker_install_binarydeb/docker.cid' +
@#         ' -v $WORKSPACE/binarydeb:/tmp/binarydeb:ro' +
@#         ' binarydeb_install.%s_%s_%s_%s_%s' % (rosdistro_name, os_name, os_code_name, arch, pkg_name),
@#         'echo "# END SECTION"',
@#     ]),
@# ))@
@(SNIPPET(
    'builder_publish-over-ssh',
    config_name='repo',
    remote_directory='%s/${JOB_NAME}__${BUILD_NUMBER}' % os_code_name,
    source_files=binarydeb_files,
    remove_prefix='binarydeb',
))@
@(SNIPPET(
    'builder_parameterized-trigger',
    project=import_package_job_name,
    parameters='\n'.join([
        'subfolder=%s/${JOB_NAME}__${BUILD_NUMBER}' % os_code_name,
        'debian_package_name=%s' % debian_package_name]),
    continue_on_failure=False,
))@
  </builders>
  <publishers>
@(SNIPPET(
    'publisher_build-trigger',
    child_projects=child_projects,
))@
@(SNIPPET(
    'publisher_description-setter',
    regexp="Package '[^']+' version: (\S+)",
    # to prevent overwriting the description of failed builds
    regexp_for_failed='ThisRegExpShouldNeverMatch',
))@
@[if notify_maintainers]@
@(SNIPPET(
    'publisher_groovy-postbuild_maintainer-notification',
))@
@[end if]@
@(SNIPPET(
    'publisher_mailer',
    recipients=notify_emails,
    dynamic_recipients=maintainer_emails,
    send_to_individuals=False,
))@
  </publishers>
  <buildWrappers>
@[if timeout_minutes is not None]@
@(SNIPPET(
    'build-wrapper_build-timeout',
    timeout_minutes=timeout_minutes,
))@
@[end if]@
@(SNIPPET(
    'build-wrapper_timestamper',
))@
@(SNIPPET(
    'build-wrapper_ssh-agent',
    credential_id=credential_id,
))@
  </buildWrappers>
</project>


Make simpleStorage always invoke user code with system.self set appropriately.

Make simpleStorage listen for onPresenceConnected as well as lookup a
presence from storage, and invoke the user code only once one of the
two has occurred. This allows it to properly handle (without a race
for the user code) system-initiated presences and those reloaded from
storage. Fixes #402.

if (typeof(std) == 'undefined')
    std = {};

if (typeof(std.simpleStorage) != 'undefined')
    throw new Error('Error in simple storage.  Should only import once.');

(function()
{
    system.require('std/shim/restore/persistService.em');
    std.simpleStorage = {  };

    var allKeysKeyname = 'allKeys';
    var prepender      = 'toPrependWith_';

    var scriptKeyName =   'pScriptKeyName';

    //contains the script to execute on restore in its script field.
    var mScript = { };

    //query backend with this index to retrieve mPres object, which lists
    //all presences that we have.
    var presKeyName   =     'pPresKeyName';
    //prevents more than one presence from being stored at a time because
    //we didn't expose batching erases and sets in the same set unfortunately
    var presOperInProgress = false;
    //last elements of the array contain next elements to be processed
    var queuedPresOpers= [];
    //Keys contain all presence sporefs.  Can use these to restore our presences.
    var mPres   = { };

    // Filled in with system-initiated presence
    var mAutomaticPresence;
    var mFinishedPresKeyLookup = false;

    // We need to monitor for new presences that we weren't
    // expecting. If we get one, then the system initiated a
    // connection without asking us. In that case, we shouldn't have
    // anything in storage and should be able to immediately invoke
    // the user's code (giving them the correct system.self). We reset
    // the callback if we find a presence to restore so that we should
    // *never* get in here unless it is due to a system-initiated
    // *connection.
    system.onPresenceConnected(
        function() {
            // If presence key lookup finished unsuccessfully, we can
            // invoke the user code now, otherwise save system.self so
            // it can be used when that lookup finishes.
            if (mFinishedPresKeyLookup)
                restorePresences();
            else
                mAutomaticPresence = system.self;
        }
    );

    std.simpleStorage.setField = function(fieldName,fieldVal,cb)
    {
        var keyName = prepender + fieldName;
        std.persist.checkpointPartialPersist(fieldVal,keyName,cb);
    };
    std.simpleStorage.write = std.simpleStorage.setField;

    std.simpleStorage.eraseField = function(fieldName, cb)
    {
        system.storageErase(prepender + fieldName, cb);
    };
    std.simpleStorage.erase = std.simpleStorage.eraseField;

    std.simpleStorage.readField = function(fieldName,cb,def)
    {
        std.persist.restoreFromAsync(prepender + fieldName,
                                     function(success,val)
                                     {
                                         if (success)
                                             cb(val);
                                         else
                                             cb(def);
                                     });
    };
    std.simpleStorage.read = std.simpleStorage.readField;

    std.simpleStorage.setPresence = function (presToRestore)
    {
        //cannot process current presence because already performing a
        //presence operation.
        if (presOperInProgress)
        {
            queuedPresOpers.unshift(['add',presToRestore]);
            return;
        }

        //locks other presence operations from occurring.
        presOperInProgress = true;

        //ensures this presence is added to our list of presences
        var presName = presToRestore.toString();
        mPres[presName] = true;

        // //callback to execute if setPresence works correctly.
        // //essentially all it does is that it begins processing
        // //next presence event.
        var finishedCB = function(success)
        {
            if (!success)
                delete mPres[presName];

            presOperInProgress = false;
            processNextPresenceEvent();
        };

        std.persist.persistMany([[mPres,presKeyName],
                                [presToRestore,presName]], finishedCB);

    };
    std.simpleStorage.writePresence = std.simpleStorage.setPresence;

    std.simpleStorage.erasePresence = function (presToErase)
    {
        //cannot process current presence because already performing a
        //presence operation.
        if (presOperInProgress)
        {
            queuedPresOpers.unshift(['remove',presToErase]);
            return;
        }

        //locks other presence operations from occurring.
        presOperInProgress = true;

        var presName = presToRestore.toString();

        //FIXME: currently cannot batch erase and write operations simultaneously.
        //As a result, mPres and actual presences in backend could get out of sync.
        //When this bug is resolved, the following finished cb will look a lot more
        //like the one in the above func.
        var finishedErasedPres = function(success)
        {
            var finishedUpdatedMPres = function(success)
            {
                if (!success)
                {
                    system.print('\nError in simpleStorage.  Could not delete presence correctly\n');
                }
                presOperInProgress = false;
                processNextPresenceEvent();
            };

            if (success)
            {
                delete mPres[presName];
                std.persist.checkpointPartialPersist(mPres,presKeyName);
                return;
            }
            presOperInProgress = false;
            processNextPresenceEvent();
        };

        system.storageErase(presName,finishedErasePres);
    };

    /**
     Pops the next operation to be processed
     */
    function processNextPresenceEvent()
    {
        if (queuedPresOpers.length == 0)
            return;

        var toProcess = queuedPresOpers.pop();

        if (toProcess[0] == 'add')
            std.simpleStorage.setPresence(toProcess[1]);
        else
            std.simpleStorage.removePresence(toProcess[1]);
    }

    std.simpleStorage.setScript = function(newScriptFunc,executeOnSet)
    {
        var newScript =  ("(" + newScriptFunc + ")();");
        if (typeof(executeOnSet) === 'undefined')
            executeOnSet = false;

        mScript.script = newScript;
        system.setRestoreScript('system.require("std/shim/restore/simpleStorage.em");');

        var cbFunc = function(){ };
        if (executeOnSet)
            cbFunc = function(){ eval(newScript);  };

        std.persist.checkpointPartialPersist(mScript,scriptKeyName,cbFunc);
    };

    std.simpleStorage.readScript = function(cb,def)
    {
        std.persist.restoreFromAsync(scriptKeyName,
                                     function(success,val)
                                     {
                                         if ((success) && ('script' in val))
                                             cb(val.script);
                                         else
                                             cb(def);
                                     });
    };

    //evaluates the script once it has been restored.
    function onRestoreScript(scriptToEval)
    {
        eval(scriptToEval);
    };

    //restores the script
    function finishOnRestorePresences()
    {
        std.simpleStorage.readScript(onRestoreScript,"");
    }

    /**
     Called when we regain our list of presences.
     Initiates restoring each presence separately.
     */
    function onPresKeyRestored(success,allPres)
    {
        // Only continue if we restored presences. Otherwise, we're
        // using an system-initiated presence and
        // system.onPresenceConnected path will make sure the user
        // code gets invoked.
        if (success) {
            // Disable onPresenceConnected: should not be getting
            // system-initiated connection if we have our own in storage.
            system.onPresenceConnected(undefined);
            mPres = allPres;
            restorePresences();
        } else {
            // We may or may not have received the onPresenceConnected
            // yet. If we did, trigger the callback, otherwise, mark a
            // flag so the onPresenceConnected callback can trigger
            // it.
            if (mAutomaticPresence) {
                system.changeSelf(mAutomaticPresence);
                restorePresences();
            }
            else
                mFinishedPresKeyLookup = true;
        }
    }

    //keeps track of all the presences that we have already restored.
    var allRestored = {};

    //called each time
    function restorePresences()
    {
        // Callback & data to handle synchronizing all the parallel restoration + connection requests
        var num_outstanding = 0;
        var finishRestorePresence = function(presName,success,pres) {
            if (!success) {
                throw new Error('Failed to restore or connect presence ' + presName + ". Bailing on restoration -- this object's script will *not* be executed.");
            }

            num_outstanding--;
            if (num_outstanding == 0) {
                finishOnRestorePresences();
            }
        };

        // Fire off requests for any presences not yet connected
        for (var s in mPres) {
            //if we've already restored this presence ignore it.
            if (s in allRestored)
                continue;
            num_outstanding++;
            allRestored[s] = true;
            std.persist.restoreFromAsync(s,std.core.bind(finishRestorePresence,undefined,s));
        }

        // Special case: no presences to restore.
        if (num_outstanding == 0) finishOnRestorePresences();
    }

    std.persist.restoreFromAsync(presKeyName,onPresKeyRestored);

})();

if (typeof(std) == 'undefined')
    std = {};

if (typeof(std.simpleStorage) != 'undefined')
    throw new Error('Error in simple storage.  Should only import once.');

(function()
{
    system.require('std/shim/restore/persistService.em');
    std.simpleStorage = {  };

    var allKeysKeyname = 'allKeys';
    var prepender      = 'toPrependWith_';

    var scriptKeyName =   'pScriptKeyName';

    //contains the script to execute on restore in its script field.
    var mScript = { };

    //query backend with this index to retrieve mPres object, which lists
    //all presences that we have.
    var presKeyName   =     'pPresKeyName';
    //prevents more than one presence from being stored at a time because
    //we didn't expose batching erases and sets in the same set unfortunately
    var presOperInProgress = false;
    //last elements of the array contain next elements to be processed
    var queuedPresOpers= [];
    //Keys contain all presence sporefs.  Can use these to restore our presences.
    var mPres   = { };

    std.simpleStorage.setField = function(fieldName,fieldVal,cb)
    {
        var keyName = prepender + fieldName;
        std.persist.checkpointPartialPersist(fieldVal,keyName,cb);
    };
    std.simpleStorage.write = std.simpleStorage.setField;

    std.simpleStorage.eraseField = function(fieldName, cb)
    {
        system.storageErase(prepender + fieldName, cb);
    };
    std.simpleStorage.erase = std.simpleStorage.eraseField;

    std.simpleStorage.readField = function(fieldName,cb,def)
    {
        std.persist.restoreFromAsync(prepender + fieldName,
                                     function(success,val)
                                     {
                                         if (success)
                                             cb(val);
                                         else
                                             cb(def);
                                     });
    };
    std.simpleStorage.read = std.simpleStorage.readField;

    std.simpleStorage.setPresence = function (presToRestore)
    {
        //cannot process current presence because already performing a
        //presence operation.
        if (presOperInProgress)
        {
            queuedPresOpers.unshift(['add',presToRestore]);
            return;
        }

        //locks other presence operations from occurring.
        presOperInProgress = true;

        //ensures this presence is added to our list of presences
        var presName = presToRestore.toString();
        mPres[presName] = true;

        // //callback to execute if setPresence works correctly.
        // //essentially all it does is that it begins processing
        // //next presence event.
        var finishedCB = function(success)
        {
            if (!success)
                delete mPres[presName];

            presOperInProgress = false;
            processNextPresenceEvent();
        };

        std.persist.persistMany([[mPres,presKeyName],
                                [presToRestore,presName]], finishedCB);

    };
    std.simpleStorage.writePresence = std.simpleStorage.setPresence;

    std.simpleStorage.erasePresence = function (presToErase)
    {
        //cannot process current presence because already performing a
        //presence operation.
        if (presOperInProgress)
        {
            queuedPresOpers.unshift(['remove',presToErase]);
            return;
        }

        //locks other presence operations from occurring.
        presOperInProgress = true;

        var presName = presToRestore.toString();

        //FIXME: currently cannot batch erase and write operations simultaneously.
        //As a result, mPres and actual presences in backend could get out of sync.
        //When this bug is resolved, the following finished cb will look a lot more
        //like the one in the above func.
        var finishedErasedPres = function(success)
        {
            var finishedUpdatedMPres = function(success)
            {
                if (!success)
                {
                    system.print('\nError in simpleStorage.  Could not delete presence correctly\n');
                }
                presOperInProgress = false;
                processNextPresenceEvent();
            };

            if (success)
            {
                delete mPres[presName];
                std.persist.checkpointPartialPersist(mPres,presKeyName);
                return;
            }
            presOperInProgress = false;
            processNextPresenceEvent();
        };

        system.storageErase(presName,finishedErasePres);
    };

    /**
     Pops the next operation to be processed
     */
    function processNextPresenceEvent()
    {
        if (queuedPresOpers.length == 0)
            return;

        var toProcess = queuedPresOpers.pop();

        if (toProcess[0] == 'add')
            std.simpleStorage.setPresence(toProcess[1]);
        else
            std.simpleStorage.removePresence(toProcess[1]);
    }

    std.simpleStorage.setScript = function(newScriptFunc,executeOnSet)
    {
        var newScript =  ("(" + newScriptFunc + ")();");
        if (typeof(executeOnSet) === 'undefined')
            executeOnSet = false;

        mScript.script = newScript;
        system.setRestoreScript('system.require("std/shim/restore/simpleStorage.em");');

        var cbFunc = function(){ };
        if (executeOnSet)
            cbFunc = function(){ eval(newScript);  };

        std.persist.checkpointPartialPersist(mScript,scriptKeyName,cbFunc);
    };

    std.simpleStorage.readScript = function(cb,def)
    {
        std.persist.restoreFromAsync(scriptKeyName,
                                     function(success,val)
                                     {
                                         if ((success) && ('script' in val))
                                             cb(val.script);
                                         else
                                             cb(def);
                                     });
    };

    //evaluates the script once it has been restored.
    function onRestoreScript(scriptToEval)
    {
        eval(scriptToEval);
    };

    //restores the script
    function finishOnRestorePresences()
    {
        std.simpleStorage.readScript(onRestoreScript,"");
    }

    /**
     Called when we regain our list of presences.
     Initiates restoring each presence separately.
     */
    function onPresKeyRestored(success,allPres)
    {
        if (success)
            mPres = allPres;

        restorePresences();
    }

    //keeps track of all the presences that we have already restored.
    var allRestored = {};

    //called each time
    function restorePresences()
    {

        // Callback & data to handle synchronizing all the parallel restoration + connection requests
        var num_outstanding = 0;
        var finishRestorePresence = function(presName,success,pres) {
            if (!success) {
                throw new Error('Failed to restore or connect presence ' + presName + ". Bailing on restoration -- this object's script will *not* be executed.");
            }

            num_outstanding--;
            if (num_outstanding == 0) {
                finishOnRestorePresences();
            }
        };

        // Fire off requests for any presences not yet connected
        for (var s in mPres) {
            //if we've already restored this presence ignore it.
            if (s in allRestored)
                continue;
            num_outstanding++;
            allRestored[s] = true;
            std.persist.restoreFromAsync(s,std.core.bind(finishRestorePresence,undefined,s));
        }

        // Special case: no presences to restore.
        if (num_outstanding == 0) finishOnRestorePresences();
    }

    std.persist.restoreFromAsync(presKeyName,onPresKeyRestored);

})();


Match docs and use `default` as name keyword

@(TEMPLATE(
    'snippet/add_generated_comment.Dockerfile.em',
    user_name=user_name,
    tag_name=tag_name,
    source_template_name=template_name,
))@
@(TEMPLATE(
    'snippet/from_base_image.Dockerfile.em',
    template_packages=template_packages,
    os_name=os_name,
    os_code_name=os_code_name,
    arch=arch,
    base_image=base_image,
    maintainer_name=maintainer_name,
))@
@
@(TEMPLATE(
    'snippet/old_release_set.Dockerfile.em',
    template_packages=template_packages,
    os_name=os_name,
    os_code_name=os_code_name,
))@
@
@(TEMPLATE(
    'snippet/setup_tzdata.Dockerfile.em',
    os_name=os_name,
    os_code_name=os_code_name,
))@
@
@{
template_dependencies = [
    'dirmngr',
    'gnupg2',
]
# add 'python3-pip' to 'template_dependencies' if pip dependencies are declared
if 'pip3_install' in locals():
    if isinstance(pip3_install, list) and pip3_install != []:
        template_dependencies.append('python3-pip')
}@
@(TEMPLATE(
    'snippet/install_upstream_package_list.Dockerfile.em',
    packages=template_dependencies,
    upstream_packages=upstream_packages if 'upstream_packages' in locals() else [],
))@
@
@(TEMPLATE(
    'snippet/setup_ros_sources.Dockerfile.em',
    os_name=os_name,
    os_code_name=os_code_name,
    ros2distro_name=ros2distro_name,
    rosdistro_name='',
    ros_version=ros_version,
))@

@(TEMPLATE(
    'snippet/install_ros_bootstrap_tools.Dockerfile.em',
    ros_version=ros_version,
))@

# setup environment
ENV LANG C.UTF-8
ENV LC_ALL C.UTF-8

# bootstrap rosdep
RUN rosdep init \
    && rosdep update

# setup colcon mixin and metadata
RUN colcon mixin add default \
      https://raw.githubusercontent.com/colcon/colcon-mixin-repository/master/index.yaml && \
    colcon mixin update && \
    colcon metadata add default \
      https://raw.githubusercontent.com/colcon/colcon-metadata-repository/master/index.yaml && \
    colcon metadata update

@[if 'pip3_install' in locals()]@
@[  if pip3_install]@
# install python packages
RUN pip3 install -U \
    @(' \\\n    '.join(pip3_install))@

@[  end if]@
@[end if]@

# install ros2 packages
ENV ROS_DISTRO @ros2distro_name
RUN apt-get update && apt-get install -y \
    @(' \\\n    '.join(ros2_packages))@  \
    && rm -rf /var/lib/apt/lists/*

@[if 'entrypoint_name' in locals()]@
@[  if entrypoint_name]@
@{
entrypoint_file = entrypoint_name.split('/')[-1]
}@
# setup entrypoint
COPY ./@entrypoint_file /

ENTRYPOINT ["/@entrypoint_file"]
@[  end if]@
@[end if]@
@{
cmds = [
'bash',
]
}@
CMD ["@(' && '.join(cmds))"]

@(TEMPLATE(
    'snippet/add_generated_comment.Dockerfile.em',
    user_name=user_name,
    tag_name=tag_name,
    source_template_name=template_name,
))@
@(TEMPLATE(
    'snippet/from_base_image.Dockerfile.em',
    template_packages=template_packages,
    os_name=os_name,
    os_code_name=os_code_name,
    arch=arch,
    base_image=base_image,
    maintainer_name=maintainer_name,
))@
@
@(TEMPLATE(
    'snippet/old_release_set.Dockerfile.em',
    template_packages=template_packages,
    os_name=os_name,
    os_code_name=os_code_name,
))@
@
@(TEMPLATE(
    'snippet/setup_tzdata.Dockerfile.em',
    os_name=os_name,
    os_code_name=os_code_name,
))@
@
@{
template_dependencies = [
    'dirmngr',
    'gnupg2',
]
# add 'python3-pip' to 'template_dependencies' if pip dependencies are declared
if 'pip3_install' in locals():
    if isinstance(pip3_install, list) and pip3_install != []:
        template_dependencies.append('python3-pip')
}@
@(TEMPLATE(
    'snippet/install_upstream_package_list.Dockerfile.em',
    packages=template_dependencies,
    upstream_packages=upstream_packages if 'upstream_packages' in locals() else [],
))@
@
@(TEMPLATE(
    'snippet/setup_ros_sources.Dockerfile.em',
    os_name=os_name,
    os_code_name=os_code_name,
    ros2distro_name=ros2distro_name,
    rosdistro_name='',
    ros_version=ros_version,
))@

@(TEMPLATE(
    'snippet/install_ros_bootstrap_tools.Dockerfile.em',
    ros_version=ros_version,
))@

# setup environment
ENV LANG C.UTF-8
ENV LC_ALL C.UTF-8

# bootstrap rosdep
RUN rosdep init \
    && rosdep update

# setup colcon mixin and metadata
RUN colcon mixin add upstream \
      https://raw.githubusercontent.com/colcon/colcon-mixin-repository/master/index.yaml && \
    colcon mixin update && \
    colcon metadata add upstream \
      https://raw.githubusercontent.com/colcon/colcon-metadata-repository/master/index.yaml && \
    colcon metadata update

@[if 'pip3_install' in locals()]@
@[  if pip3_install]@
# install python packages
RUN pip3 install -U \
    @(' \\\n    '.join(pip3_install))@

@[  end if]@
@[end if]@

# install ros2 packages
ENV ROS_DISTRO @ros2distro_name
RUN apt-get update && apt-get install -y \
    @(' \\\n    '.join(ros2_packages))@  \
    && rm -rf /var/lib/apt/lists/*

@[if 'entrypoint_name' in locals()]@
@[  if entrypoint_name]@
@{
entrypoint_file = entrypoint_name.split('/')[-1]
}@
# setup entrypoint
COPY ./@entrypoint_file /

ENTRYPOINT ["/@entrypoint_file"]
@[  end if]@
@[end if]@
@{
cmds = [
'bash',
]
}@
CMD ["@(' && '.join(cmds))"]


keep end section in generated script

keep end section in generated script

@{
# same logic as in from_base_image.Dockerfile.em
if arch in ['i386', 'armhf', 'arm64']:
    base_image = 'osrf/%s_%s:%s' % (os_name, arch, os_code_name)
else:
    base_image = '%s:%s' % (os_name, os_code_name)
}@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        'echo "# BEGIN SECTION: Check docker status"',
        'echo "Testing trivial docker invocation..."',
        'docker run --rm %s true ; echo "\'docker run\' returned $?"' % base_image,
    ]),
))@
@(SNIPPET(
    'builder_system-groovy',
    command=
"""// DISABLE SLAVE IF OUTPUT INDICATES DOCKER RUN PROBLEMS
import hudson.slaves.OfflineCause.UserCause
import java.io.BufferedReader
import java.util.regex.Pattern
import org.jvnet.hudson.plugins.groovypostbuild.GroovyPostbuildAction

pattern = Pattern.compile("'docker run' returned [^0].*")

r = build.getLogReader()
br = new BufferedReader(r)
def line
while ((line = br.readLine()) != null) {
    matcher = pattern.matcher(line)
    if (matcher.matches()) {
        println "'docker run' failed"

        // mark slave as offline and log event
        def node = build.getBuiltOn()
        def computer = node.toComputer()
        computer.setTemporarilyOffline(true, new UserCause(null, "'docker run' failed"))

        // the job will be rescheduled automatically if it has corresponding flag set in the project configuration

        // add badge to build
        build.getActions().add(GroovyPostbuildAction.createInfoBadge("'docker run' failed, disabled slave '" + computer.name + "', rescheduled job"))

        // abort this build
        throw new InterruptedException()
    }
}
println "docker seems operational, continuing"
""",
    script_file=None,
    classpath='',
))@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        'echo "# END SECTION"',
    ]),
))@

@{
# same logic as in from_base_image.Dockerfile.em
if arch in ['i386', 'armhf', 'arm64']:
    base_image = 'osrf/%s_%s:%s' % (os_name, arch, os_code_name)
else:
    base_image = '%s:%s' % (os_name, os_code_name)
}@
@(SNIPPET(
    'builder_shell',
    script='\n'.join([
        'echo "# BEGIN SECTION: Check docker status"',
        'echo "Testing trivial docker invocation..."',
        'docker run --rm %s true ; echo "\'docker run\' returned $?"' % base_image,
    ]),
))@
@(SNIPPET(
    'builder_system-groovy',
    command=
"""// DISABLE SLAVE IF OUTPUT INDICATES DOCKER RUN PROBLEMS
import hudson.slaves.OfflineCause.UserCause
import java.io.BufferedReader
import java.util.regex.Pattern
import org.jvnet.hudson.plugins.groovypostbuild.GroovyPostbuildAction

pattern = Pattern.compile("'docker run' returned [^0].*")

r = build.getLogReader()
br = new BufferedReader(r)
def line
while ((line = br.readLine()) != null) {
    matcher = pattern.matcher(line)
    if (matcher.matches()) {
        println "'docker run' failed"

        // mark slave as offline and log event
        def node = build.getBuiltOn()
        def computer = node.toComputer()
        computer.setTemporarilyOffline(true, new UserCause(null, "'docker run' failed"))

        // the job will be rescheduled automatically if it has corresponding flag set in the project configuration

        // add badge to build
        build.getActions().add(GroovyPostbuildAction.createInfoBadge("'docker run' failed, disabled slave '" + computer.name + "', rescheduled job"))

        // abort this build
        throw new InterruptedException()
    }
}
println "docker seems operational, continuing"

println "# END SECTION"
""",
    script_file=None,
    classpath='',
))@