org.olat.resource.lock.pessimistic.PessimisticLockManager.java Source code

Java tutorial

Introduction

Here is the source code for org.olat.resource.lock.pessimistic.PessimisticLockManager.java

Source

/**
 * 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.resource.lock.pessimistic;

import java.util.List;

import org.hibernate.LockMode;
import org.olat.core.commons.persistence.DBFactory;
import org.olat.core.commons.persistence.DBQuery;
import org.olat.core.configuration.Initializable;
import org.olat.core.logging.AssertException;
import org.olat.core.logging.Tracing;
import org.olat.core.manager.BasicManager;

/**
 * Description:<br>
 * implementation for pessimistic locking.<br>
 * Do not use this class directly. please use Syncer or Locker via CoordinatorManager!
 * <P>
 * Initial Date: 25.10.2007 <br>
 * 
 * @author Felix Jost, http://www.goodsolutions.ch
 */
public class PessimisticLockManager extends BasicManager implements Initializable {

    private static PessimisticLockManager INSTANCE;
    private final String ASSET_INSERT_LOCK = "SYS_plock_global";
    private boolean initDone = false;

    /**
     * [used by spring]
     */
    private PessimisticLockManager() {
        INSTANCE = this;
    }

    public static PessimisticLockManager getInstance() {
        return INSTANCE;
    }

    @Override
    public void init() {
        // make sure that the resource (= row in our table) to lock the creation of new assets exists
        PLock gLock = findPLock(ASSET_INSERT_LOCK);
        if (gLock == null) {
            // need to create it
            gLock = createPLock(ASSET_INSERT_LOCK);
            savePLock(gLock);
        }
        DBFactory.getInstance().intermediateCommit();
        initDone = true;
    }

    private PLock findPLock(final String asset) {
        final DBQuery q = DBFactory.getInstance().createQuery(
                "select plock from org.olat.resource.lock.pessimistic.PLockImpl as plock where plock.asset = :asset");
        q.setParameter("asset", asset);
        q.setLockMode("plock", LockMode.UPGRADE);
        final List res = q.list();
        if (res.size() == 0) {
            return null;
        } else {
            return (PLock) res.get(0);
        }
    }

    private PLock createPLock(final String asset) {
        return new PLockImpl(asset);
    }

    private void savePLock(final PLock plock) {
        DBFactory.getInstance().saveObject(plock);
    }

    /**
     * do not use this class directly. please use Syncer or Locker via CoordinatorManager!
     * 
     * @param asset
     * @return
     */
    public PLock findOrPersistPLock(final String asset) {
        if (!initDone) {
            throw new AssertException(
                    "init not called yet - make sure the ClusterModule is enabled in your olat.local.properties file");
        }
        if (Tracing.isDebugEnabled(PessimisticLockManager.class)) {
            Tracing.logDebug("findOrPersistPLock START asset=" + asset, PessimisticLockManager.class);
        }
        PLock plock = findPLock(asset);
        if (Tracing.isDebugEnabled(PessimisticLockManager.class)) {
            if (plock == null) {
                Tracing.logDebug("findOrPersistPLock PLock not found", PessimisticLockManager.class);
            } else {
                Tracing.logDebug("findOrPersistPLock found and locked PLock: " + plock,
                        PessimisticLockManager.class);
            }
        }
        // if not found, persist it.
        if (plock == null) {
            // synchronize the findOrCreate by using the special row with the global-lock-asset
            // locks the global lock - which is only used to sync creation of new resource entries, so that those can later be locked.
            findPLock(ASSET_INSERT_LOCK);
            if (Tracing.isDebugEnabled(PessimisticLockManager.class)) {
                Tracing.logDebug("findOrPersistPLock global insert lock locked", PessimisticLockManager.class);
            }
            // need to read again within the protected region
            plock = findPLock(asset);
            if (plock == null) {
                if (Tracing.isDebugEnabled(PessimisticLockManager.class)) {
                    Tracing.logDebug("findOrPersistPLock creating new plock: " + asset,
                            PessimisticLockManager.class);
                }
                plock = createPLock(asset);
                if (Tracing.isDebugEnabled(PessimisticLockManager.class)) {
                    Tracing.logDebug("findOrPersistPLock created new plock: " + asset,
                            PessimisticLockManager.class);
                }
                savePLock(plock);
                if (Tracing.isDebugEnabled(PessimisticLockManager.class)) {
                    Tracing.logDebug("findOrPersistPLock saved new plock: " + asset, PessimisticLockManager.class);
                }
            } // else plock got created by another thread in the meantime

            // some notes:
            // takes advantage of the fact that the select for update blocks a transaction when a lock is already acquired.
            //

            // since we have concurrent access here, we could have many threads which try to create
            // the entry to later lock upon.

            // we therefore could
            // a) lock on a olat-wide lock
            // or b) catch the exception - and continue, since we know that the row already exists
            // even c) start a new connection and set serializable isolation level..

            // in a cluster, each vm syncs via synchronized() first, so that there is only one concurrent access to the PLockManager from one node at a given time.
            // -> we have maximal num-of-cluster concurrent accesses, e.g. 3-5

            // a: performance, should only occur once for a resource: the first time a lock for a certain resource is accessed.
            // b) is the transaction still safe to continue? what about hibernate first level cache etc. hibernate docs says in general we'd need to close the session.

            // -> go for solution a.

        } // else found
        return plock;
    }

}