2009-02-18 36 views
1

我正在開發一個服務,該服務在需要執行一些週期性作業的Java應用服務器場中運行(比如每2分鐘運行一次)。該服務必須與外部實體連接,並且需要同步不同的實例,以便在給定時間只有其中一個工作在該工作上。由於服務這項工作中採用了DB,我認爲基於一個簡單的數據庫表實現同步的:簡單的基於數據庫的實例同步

id, owner, stamp 

其中id是鎖的ID,所有者是當前所有者和郵票是被鎖定的時間。

的方法是:

tryLock(id, maxAge, owner) - to try to lock a record or break an old record 
refresh(id, owner) - to update the stamp to signal we're still around working on the job 
release(id, owner) - to release the lock 

你將如何實現這一點?

編輯:刪除我的實現,我會後,我想出了以下實現的「答案」

回答

1

,但我不知道這是否可以處理所有的情況(我不完全確定我正確使用BeanManagedTransaction)。另外,如果您認爲這種同步問題可以用更簡單的方式處理,請指出我的正確方向。

@Service(objectName=Sync.EjbName) 
@Management(SyncMgt.class) 
@TransactionManagement(value=TransactionManagementType.BEAN) 
public class SyncSvc implements SyncMgt { 

    @PersistenceContext 
    protected EntityManager entityManager_; 
    @Resource 
    protected UserTransaction utx_; 

    @TransactionAttribute(TransactionAttributeType.REQUIRED) 
    private boolean update(SyncRecord sr, String owner) { 
     Date stamp = (owner != null) ? new Date() : null; 
     Query q; 
     if (sr.getOwner() != null) { 
      q = entityManager_.createQuery("UPDATE SyncRecord sr SET sr.owner = :newOwner, sr.stamp = :stamp WHERE sr.id = :id AND sr.owner = :origOwner AND sr.stamp = :origStamp"); 
      q.setParameter("origOwner", sr.getOwner()); 
      q.setParameter("origStamp", sr.getStamp()); // make it fail if someone refreshed in the meantime 
     } 
     else { 
      q = entityManager_.createQuery("UPDATE SyncRecord sr SET sr.owner = :newOwner, sr.stamp = :stamp WHERE sr.id = :id AND sr.owner IS NULL"); 
     } 
     q.setParameter("id", sr.getId()); 
     q.setParameter("newOwner", owner); 
     q.setParameter("stamp", stamp); 
     int res = q.executeUpdate(); 
     if (res != 1) { 
      return false; 
     } 
     return true; 
    } 

    @TransactionAttribute(TransactionAttributeType.REQUIRED) 
    private boolean tryLockImpl(String id, long maxAge, String owner) { 
     SyncRecord sr = entityManager_.find(SyncRecord.class, id); 
     if (sr == null) { 
      // no record yet, create one 
      sr = new SyncRecord(id, owner); 
      sr.touch(); 
      entityManager_.persist(sr); 
      entityManager_.flush(); 
      return true; 
     } 
     // found a SyncRecord, let's see who owns it 
     if (owner.equals(sr.getOwner())) { 
      // log some warning, re-locking old lock, should use refresh instead 
      return update(sr, owner); 
     } 
     if (sr.getOwner() == null) { 
      // sr is not held by anyone, safe to grab it 
      return update(sr, owner); 
     } 
     // someone else holds it, let's check the age 
     if (maxAge >= 0) { 
      long maxAgeStamp = System.currentTimeMillis() - maxAge; 
      if (sr.getStamp().getTime() < maxAgeStamp) { 
       if (update(sr, owner)) { 
        return true; 
       } 
       return false; 
      } 
     } 
     return false; 
    } 

    // Sync impl: 

    /** 
    * Try to lock "id" for "owner" 
    * If the lock is held by someone else, but is older than maxAge, break it 
    */ 
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 
    public boolean tryLock(String id, long maxAge, String owner) { 
     if (id == null) 
      throw new IllegalArgumentException("id is null"); 
     try { 
      utx_.begin(); 
      if (tryLockImpl(id, maxAge, owner)) { 
       utx_.commit(); 
       return true; 
      } 
     } 
     catch (EntityExistsException e) { 
      // failed to lock, someone beat us to it 
     } 
     catch (Throwable e) { 
      // some fishy error, raise alarm, log, etc 
     } 
     try { 
      utx_.rollback(); 
     } 
     catch (Throwable e) { 
      // log the error, not much else we can do at this point 
     } 
     return false; 
    } 

    /** 
    * Refresh lock "id" belonging to "owner" (update its stamp) 
    */ 
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 
    public boolean refresh(String id, String owner) { 
     if (id == null) 
      throw new IllegalArgumentException("id is null"); 
     try { 
      utx_.begin(); 
      SyncRecord sr = entityManager_.find(SyncRecord.class, id); 
      if (sr == null || !owner.equals(sr.getOwner())) { 
       utx_.rollback(); 
       return false; 
      } 
      if (update(sr, owner)) { 
       utx_.commit(); 
       return true; 
      } 
     } 
     catch (Throwable e) { 
      // some fishy error, raise alarm, log, etc 
     } 
     try { 
      utx_.rollback(); 
     } 
     catch (Throwable e) { 
      // log the error, not much else we can do at this point 
     } 
     return false; 
    } 

    /** 
    * release lock "id" held by "owner" 
    */ 
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 
    public void release(String id, String owner) { 
     if (id == null) 
      throw new IllegalArgumentException("id is null"); 
     try { 
      utx_.begin(); 
      SyncRecord sr = entityManager_.find(SyncRecord.class, id); 
      if (sr == null || !owner.equals(sr.getOwner())) { 
       // we don't own it 
       utx_.rollback(); 
       return; 
      } 
      if (update(sr, null)) { 
       utx_.commit(); 
       return; 
      } 
     } 
     catch (Throwable e) { 
      // some fishy error, raise alarm, log, etc 
     } 
     try { 
      utx_.rollback(); 
     } 
     catch (Throwable e) { 
      // log the error, not much else we can do at this point 
     } 
    } 

    // LifeCycle impl: 

    public void start() {} 
    public void stop() {} 

}