package com.psddev.cms.db; import java.util.Date; import java.util.UUID; import com.psddev.dari.db.Query; import com.psddev.dari.db.Record; import com.psddev.dari.db.Recordable; import com.psddev.dari.db.State; import com.psddev.dari.util.ObjectUtils; import com.psddev.dari.util.UuidUtils; public class ContentLock extends Record { private Date createDate; private UUID contentId; private Recordable owner; public Date getCreateDate() { return createDate; } public void setCreateDate(Date createDate) { this.createDate = createDate; } public UUID getContentId() { return contentId; } public void setContentId(UUID contentId) { this.contentId = contentId; } public Object getOwner() { return owner; } public void setOwner(Object owner) { this.owner = (Recordable) owner; } /** * {@link ContentLock} utility methods. */ public static class Static { private static UUID createLockId(Object content, String aspect) { return UuidUtils.createVersion3Uuid( "cms.contentLock/" + State.getInstance(content).getId() + "/" + ObjectUtils.firstNonNull(aspect, "")); } /** * Returns the lock associated with the given {@code aspect} of the * given {@code content}. * * @param content Can't be {@code null}. * @param aspect If {@code null}, it's equivalent to an empty string. * @return May be {@code null} if there is no lock associated, or if * the lock's owner is either archived or deleted. */ public static ContentLock findLock(Object content, String aspect) { ContentLock lock = Query .from(ContentLock.class) .where("_id = ?", createLockId(content, aspect)) .master() .noCache() .first(); if (lock != null) { Object owner = lock.getOwner(); // Owner is deleted. if (owner == null) { unlock(content, null, null); return null; // Owner is archived. } else if (State.getInstance(owner).as(Content.ObjectModification.class).isTrash()) { return null; } } return lock; } /** * Tries to lock the given {@code aspect} of the given {@code content} * and associate it to the given {@code newOwner}. * * @param content Can't be {@code null}. * @param aspect If {@code null}, it's equivalent to an empty string. * @param newOwner Can't be {@code null}. * @return Never {@code null}. */ public static ContentLock lock(Object content, String aspect, Object newOwner) { UUID lockId = createLockId(content, aspect); while (true) { ContentLock lock = Query .from(ContentLock.class) .where("_id = ?", lockId) .master() .noCache() .first(); if (lock != null) { Object owner = lock.getOwner(); // Unlock if owner doesn't exist or is archived if (owner == null || State.getInstance(owner).as(Content.ObjectModification.class).isTrash()) { unlock(content, null, owner); } else { return lock; } } lock = new ContentLock(); lock.getState().setId(lockId); lock.setCreateDate(new Date()); lock.setContentId(State.getInstance(content).getId()); lock.setOwner(newOwner); lock.saveImmediately(); } } /** * Unlocks the given {@code aspect} of the given {@code content} * if it's associated with the given {@code owner}. * * @param content Can't be {@code null}. * @param aspect If {@code null}, it's equivalent to an empty string. * @param owner If {@code null}, always unlocks. */ public static void unlock(Object content, String aspect, Object owner) { ContentLock lock = Query .from(ContentLock.class) .where("_id = ?", createLockId(content, aspect)) .first(); if (lock != null && (owner == null || owner.equals(lock.getOwner()))) { lock.delete(); } } } }