Package org.olat.commons.coordinate.cluster.lock

Source Code of org.olat.commons.coordinate.cluster.lock.ClusterLocker

/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <p>
*/
package org.olat.commons.coordinate.cluster.lock;

import java.util.ArrayList;
import java.util.List;

import org.olat.core.CoreSpringFactory;
import org.olat.core.commons.persistence.DBFactory;
import org.olat.core.gui.control.Event;
import org.olat.core.id.Identity;
import org.olat.core.id.OLATResourceable;
import org.olat.core.logging.AssertException;
import org.olat.core.logging.DBRuntimeException;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.util.SignOnOffEvent;
import org.olat.core.util.UserSession;
import org.olat.core.util.coordinate.LockEntry;
import org.olat.core.util.coordinate.LockResult;
import org.olat.core.util.coordinate.LockResultImpl;
import org.olat.core.util.coordinate.Locker;
import org.olat.core.util.coordinate.PersistentLockManager;
import org.olat.core.util.coordinate.Syncer;
import org.olat.core.util.coordinate.SyncerCallback;
import org.olat.core.util.event.EventBus;
import org.olat.core.util.event.GenericEventListener;
import org.olat.core.util.resource.OresHelper;
import org.olat.resource.lock.pessimistic.PessimisticLockManager;

/**
* Description:<br>
* the cluster implementation for the Locker.
* It uses a database table oc_lock to perform the locking.
*
* <P>
* Initial Date:  21.09.2007 <br>
* @author Felix Jost, http://www.goodsolutions.ch
*/
public class ClusterLocker implements Locker, GenericEventListener {
  protected OLog log = Tracing.createLoggerFor(this.getClass());
  PersistentLockManager plm = null;

  private Syncer syncer;
  private EventBus eventBus;
 
  /**
   * [used by spring]
   *
   */
  public ClusterLocker() {
    // used by spring.
  }
 
  /**
   * [used by spring]
   *
   */
  public void init() {
    // called by spring.
    // register for sign-off event in order to release all locks for that user
    eventBus.registerFor(this, null,
        OresHelper.createOLATResourceableType(UserSession.class));
  }
 
  //cluster:::::: on init of olat system, clear all locks?? but only the one from node in question?
 
 
  public LockResult acquireLock(final OLATResourceable ores, final Identity requestor, final String locksubkey) {
    final String asset = OresHelper.createStringRepresenting(ores, locksubkey);
   
    LockResult res = syncer.doInSync(ores, new SyncerCallback<LockResult>(){
      public LockResult execute() {
        ClusterLockManager cm = ClusterLockManager.getInstance();
        LockResultImpl lres;
        LockImpl li = cm.findLock(asset);
        if (li == null) { // fine, we can lock it
          li = cm.createLockImpl(asset, requestor);
          cm.saveLock(li);
          LockEntry le = new LockEntry(li.getAsset(), li.getCreationDate().getTime(), li.getOwner());
          lres = new LockResultImpl(true, le);
        } else {
          // already locked by a user.
          // if that user is us, we can reacquire it
          LockEntry le = new LockEntry(li.getAsset(), li.getCreationDate().getTime(), li.getOwner());
          if (requestor.getName().equals(li.getOwner().getName())) {
            // that's us -> success (asset, owner is the same, and we leave creationdate to when the lock was originally acquired, not when it was reacquired.
            lres = new LockResultImpl(true, le);       
          } else {
            lres = new LockResultImpl(false, le);
          }
        }   
        return lres;
      }});
   
    return res;
  }
 
  /**
   * receives all sign on / sign off events so it can release locks of users
   * which have or are logged off
   *
   * @see org.olat.core.util.event.GenericEventListener#event(org.olat.core.gui.control.Event)
   */
  public void event(Event event) {
    SignOnOffEvent se = (SignOnOffEvent) event;
    if (!se.isSignOn() && se.isEventOnThisNode()) {
      // it is a "logout" event - we are only interested in logout events
      // and it is from our VM => only release all locks from within one VM
      String identName = se.getIdentityName();
      // release all locks held by the identity that has just logged out.
      // (assuming one user has only one session (logged in with one browser only): otherwise (as in singlevm, too)
      // since the lock is reentrant, a lock could be freed while a session still is in a locked workflow (2x lock and then once freed)
      ClusterLockManager cm = ClusterLockManager.getInstance();
      try {
        cm.releaseAllLocksFor(identName);
      } catch (DBRuntimeException dbEx) {
        log.warn("releaseAllLocksFor failed, close session and try it again for identName=" + identName);
        //TODO: 2010-04-23 Transactions [eglis]: OLAT-4318: this rollback has possibly unwanted
        //      side effects, as it rolls back any changes with this transaction during this
        //      event handling. Nicer would be to be done in the outmost-possible place, e.g. dofire()
        DBFactory.getInstance().rollbackAndCloseSession();
        // try again with new db-session
        log.info("try again to release all locks for identName=" + identName);
        cm.releaseAllLocksFor(identName);
        log.info("Done, released all locks for identName=" + identName);
      }
    }
  }
 

  public boolean isLocked(OLATResourceable ores, String locksubkey) {
    final String asset = OresHelper.createStringRepresenting(ores, locksubkey);
    ClusterLockManager cm = ClusterLockManager.getInstance();
    LockImpl li = cm.findLock(asset);
    return (li != null);
  }

  public void releaseLock(LockResult lockResult) {
    // if the lock has not been acquired, then nothing is to be released -
    // return silently to make cleaning up easier
    if (!lockResult.isSuccess()) {
      return;
    }
    LockEntry le = ((LockResultImpl) lockResult).getLockEntry();
   
    String asset = le.getKey();
    Identity releaseRequestor = le.getOwner();
   
    final ClusterLockManager cm = ClusterLockManager.getInstance();
   
    // cluster:: change to useage with syncer, but we don't have the olatresourceable yet
    PessimisticLockManager.getInstance().findOrPersistPLock(asset);

    LockImpl li = cm.findLock(asset);
    if (li == null) {
      // do nothing - since this lock may have been one that was cleared when restarting the vm
    } else {
      // check that entry was previously locked by the same user that now wants to release the lock.
      Identity ownwer = li.getOwner();
      if (releaseRequestor.getName().equals(ownwer.getName())) {
        // delete the lock
        cm.deleteLock(li);
      } else {
        throw new AssertException("cannot release lock since the requestor of the release ("+
            releaseRequestor.getName()+") is not the owner ("+ownwer.getName()+") of the lock ("+asset+")");
      }
    }
  }
 
  public List<LockEntry> adminOnlyGetLockEntries() {
    ClusterLockManager cm = ClusterLockManager.getInstance();
    List<LockImpl> li = cm.getAllLocks();
    List<LockEntry> res = new ArrayList<LockEntry>(li.size());
    for (LockImpl impl : li) {
      res.add(new LockEntry(impl.getAsset(), impl.getCreationDate().getTime(), impl.getOwner()));
    }
    return res;
  }
 
  public LockResult aquirePersistentLock(final OLATResourceable ores, final Identity ident, final String locksubkey) {
    LockResult res = syncer.doInSync(ores, new SyncerCallback<LockResult>(){
      public LockResult execute() {
        if (plm == null) { // lazy init because the impl is not defined in the core spring config
          plm = (PersistentLockManager) CoreSpringFactory.getBean(PersistentLockManager.class);
        }
        LockResult ares = plm.aquirePersistentLock(ores, ident, locksubkey);
        return ares;
      }
    });
    return res;
  }

  public void releasePersistentLock(LockResult lockResult) {
    // cluster_ok: since a certain LockResult can only be from one user/session that previously acquired the lock
    // if the lock has not been acquired, do nothing
    if (!lockResult.isSuccess())
      return;

    if (plm == null) { // lazy init because the impl is not defined in the core spring config
      plm = (PersistentLockManager) CoreSpringFactory.getBean(PersistentLockManager.class);
    }
    // delegate to the concrete implementation
    plm.releasePersistentLock(lockResult);
  }

  /**
   * [used by spring]
   * @param syncer
   */
  public void setSyncer(Syncer syncer) {
    this.syncer = syncer;
  }

  /**
   * [used by spring]
   */
  public void setEventBus(EventBus eventBus) {
    this.eventBus = eventBus;
  }

}
TOP

Related Classes of org.olat.commons.coordinate.cluster.lock.ClusterLocker

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.