jLockit

jLockit is a small lightweight architecture for object locking in Java. It allows developers to mark objects as being locked and then have their code check if the object is locked or not. It also provides a small work flow engine that can plugs into your applications existing architecture so that code may or may not be executed based upon the lock status of some object.

jLockit is based on the EhCache caching system. This is because EhCache can be plugged into Terracotta to provide a distributed cache which means that locks created in jLockit can be distributed among JVM’s for a distributed locking mechanism.

I wrote jLockit because I’ve had a need for a locking architecture for so many different projects and I was always amazed that I could never find a small already thought-out locking system.

jLockit can be found on SourceForge at https://sourceforge.net/projects/jlockit/

Making an object Lockable

To make an object lockable, have the object implement ILockIdentifiable and ILock like so:

public class Employee
    implements ILockIdentifiable, ILock
{
    private long id;
    private String firstName;
    private String lastName;

    public Employee() {}

    public String getLockDescription() { return “Employee +firstName+” “+lastName; }
    public String getLockKey() { return LockKeyUtil.generateDomainKey(this.getClass(), getId()); }
    public ILockInfo getLockInfo() { return LockUtil.getLockInfo(this); }
    public ILockInfo lock(String lockOwner) { return LockUtil.lock(this, lockOwner); }
    public ILockInfo unlock() { return LockUtil.unlock(this); }
}

As you can see, a LockUtil object exists to assist in the actual locking and status reporting of the lock.
LockKeyUtil exists to assist in the generation of keys to uniquely identify the object.

This framework lends itself to being placed in a parent object for code reusability like so:

public abstract class AbstractLockable
    implements ILockIdentifiable, ILock
{
    public abstract long getId();

    public String getLockKey() { return LockKeyUtil.generateDomainKey(this.getClass(), getId()); }
    public ILockInfo getLockInfo() { return LockUtil.getLockInfo(this); }
    public ILockInfo lock(String lockOwner) { return LockUtil.lock(this, lockOwner); }
    public ILockInfo unlock() { return LockUtil.unlock(this); }
}

public class Employee
    extends AbstractLockable
{
    private long id;
    private String firstName;
    private String lastName;

    public getLockDescription() { return “Employee +firstName+” “+lastName; }
    public long getId() { return id; }
}

Checking a Lock

Because the object encapsulates the locking we can easily check the lock status of an object by asking it if it is locked or not:

if (employee.getLockInfo().isLocked())...

or, if you want to see if the person who owns the lock is the current user (a common check since you want that person to be able to operate against the object:

if (employee.getLockInfo().isMyLock(currentUser))...

Global locks

jLockit also supports the idea of Global locks. A Global lock is a lock that is not specific to a specific instance of an object, but is more of a lock for more generic purposes (such as not allowing anyone else to do anything to a specific piece of the application while someone else is).

Global locks work much like a regular lock except that you lock a singleton object.

public class NoOneCanEditEmployeesLock
    extends GlobalLockAdapter
{
    private static final String DESC = “Edit Employee”;
    /* Make a single instance since it is supposed to be global */
    private static final NoOneCanEditEmployeesLock INSTANCE =  new NoOneCanEditEmployeesLock();

    private NoOneCanEditEmployeesLock() {}

    public String getLockDescription() { return DESC; }

    public static NoOneCanEditEmployeesLock getInstance() { return INSTANCE; }
}

public class SomeBusinessLogic {
    public void lockEmployeeEdits() {
        NoOneCanEditEmployeesLock.getInstance().lock(“GLOBAL”);
    }

    public void editEmployee() {
        // If it is locked, don’t do anything
        if (NoOneCanEditEmployeesLock.getInstance().getLockInfo().isLocked()) return;
        
        // do the edit 
    }
}

If this method is a bit to ‘heavy’ jLockit also provides a StringLockable object that allows locks based on simple strings. The above, using StringLockable, could be implemented like so:

public class Constants {
     public static final StringLockable LOCK_NO_EMPLOYEES = 
        new StringLockable("noEmployees", "No Employees can be edited");
}
public class SomeBusinessLogic {
    public void lockEmployeeEdits() {
        Constants.LOCK_NO_EMPLOYEES.lock(“GLOBAL”);
    }

    public void editEmployee() {
        // If it is locked, don’t do anything
        if (Constants.LOCK_NO_EMPLOYEES.getLockInfo().isLocked()) return;
        
        // do the edit 
    }
}

Work flow

jLockit comes with a work flow engine that allows developers to restrict code execution based upon object lock status. This piece is admittedly a bit more complex than the above code to allow maximum flexibility.

The work flow engine, LockDispatcher follows a event/listener model for code execution. Let’s first look at how you could have some executable code be influenced by the lock state of an object:

public class MyObject {
    implements ILockAcquireEventListener<MyLockEvent, MyReturnObject>
    ILockExecutable<MyLockEvent, MyReturnObject >
    ILockReleaseEventListener<MyLockEvent, MyReturnObject>

    public boolean acquireLock(MyLockEvent _evt) {
        //some code to see if we can get the lock on the object like
        String currentUser = _evt.getCurrentUser();
        Employee employee = Session.get(_evt.getEmployeeId(), Employee.class);
        // true if currentUser got the lock, false is someone owns it
        return employee.lock(currentUser).isMyLock(currentUser); 
    }

    public MyReturnObject handleAcquireLockFailure(MyLockEvent _evt) {
        return new MyReturnObject("Employee "+_evt.getEmployeeId()+" already locked");
    }

    public MyReturnObject execute(MyLockEvent _evt) {
        // do something like update the user
        return MyReturnObject("Employee updated");;
    }
}

The code above, if called in the context of the work flow (more on that in a minute) would execute() only if a lock could be acquired on the Employee object. This would be useful for something like only allowing one person to update a specific Employee record at one time.

To call the code under the work flow engine:

public class MyWorkflowSystem {
    public void something() {
        MyObject mo = new MyObject();
        MyLockEvent le = new MyLockEvent();
        le.setEmployeeId(someId);
        le.setCurrentUser(currentUser);

        LockEventContext<MyLockEvent, MyReturnObject > lec = new LockEventContext<MyLockEvent, MyReturnObject>();
        lec.setExecutable(mo);
        lec.setAcquireEventListener(mo);
        MyReturnObject wasExecuteSuccessful = LockDispatcher.dispatchLockEvent(lec);
    }
}

The work flow and how is it implemented is discussed in much more detail in the documentation found at the SourceForge project page.

Realistically your code would not be creating events and calling LockDispatcher every time but would instead be implemented in something like an Interceptor (in Struts 2) or some type of wrapper around calling code. Doing something with Java Annotations would also be a very clean way of doing things (maybe in a future version of the library?)

Leave a Reply

Your email address will not be published.