Webservices and locking

2
Consider the following microflow: model share It is an exposed webservice called by another application. I want to make sure that the business logic is only executed when the parent microflow has the (one) lock. However, when I apply no authentication in the webservice, the WaitForLock action throws a nullpointer exception (AFAIK because it has no session). When I apply authentication and create a webservice user and then trigger the webservice twice using those credentials, they both get past the locking system! (using delays and breakpoints to make sure the lock has not been released yet). The only way I've seen this working is by placing the microflow behind a button and using different (browser) sessions to trigger it from a user interface. Am I using it wrong? Is this not supported? Do I have to write a custom action for this? EDIT: Disallowing concurrency is not enough. The business logic is called in multiple webservices and disallowing concurrency on the different webservice microflows and the business logic microflows can still cause issues if the timing is just right (or wrong..). Especially since the webservice microflows are going to be triggered nearly simultaneously. Solution: In the end we went with Japer's Semaphore. Just for future reference and anyone else who has to use it: we had to decrease the (hardcoded) timeout in this action we had to use the semaphore in combination with the StartTransaction and EndTransaction from the community commons to enforce that the changes were sent to the database before the lock was released.
asked
3 answers
3

The lock of the object is connected to the session of the user. If you waitforlock with the same webservice user you will get the lock of the other webservice call. You need to adapt the locking mechanism .

EDIT:

    public static synchronized Boolean acquireLock(IContext context, IMendixObject item) 
{
    if (!isLocked(item)) {
        //if (!context.getSession().getUser().getName().equals(getLockOwner(item))) 
        locks.put(item.getId().toLong(), context.getSession());
        return true;
    }
    //else if (locks.get(item.getId().toLong()).equals(context.getSession()))
    //  return true; //lock owned by this session
    return false;
}
answered
1

I had a look at the Semaphore action posted by Jasper. I have a few issues with it, but the concept seems like the way to go here. If you do implement a solution like this, please have someone with knowledge of Java concurrency take a look at your final solution. If you do not you're likely to introduce possible deadlocks, have a solution that does not work in edge cases you hadn't thought of, and/or leaking memory.

On a completely different note, I am not sure I like the fact that you throw an Exception when you're not able to acquire the lock. This does not seem like an exceptional result given that you are expecting this to happen from time to time. If the webservice definition does not allow handling this gracefully then this is probably the way to go though.

edit PS: consider if it's okay that this will only work as long as your application runs on one server. If you need to scale up servers in the future, this will break.

answered
0

May be a workaround is possible. Create a seperate table where you store the UID of the objects currently used by the webservices. Do a check there if the UID is already in the table. If so break otherwise let the webservice continue. It's a bit more of a hasle to create / delete the objects but I think this should work.

Regards,

Ronald

answered