/* (c) 2015 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.cluster.hazelcast;
import static java.lang.String.format;
import static org.geoserver.cluster.hazelcast.HazelcastUtil.nodeId;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.geoserver.config.LockProviderInitializer;
import org.geoserver.platform.resource.LockProvider;
import org.geoserver.platform.resource.Resource.Lock;
import org.geotools.util.logging.Logging;
import org.springframework.beans.factory.InitializingBean;
import com.google.common.base.Preconditions;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.ILock;
/**
* A {@link LockProvider} implementation on top of Hazelcast's {@link ILock} distributed locks.
* <p>
* A Spring bean of this type shall be configured in the project's {@code applicationContext.xml}
* spring configuration file in order for {@link LockProviderInitializer} to find it, and for the
* global configuration page (web UI) to find it and present it as a locking provider option.
*/
public class HzLockProvider implements LockProvider, InitializingBean {
private static final Logger LOGGER = Logging.getLogger(HzLockProvider.class);
private HzCluster cluster;
/**
* {@code cluster} property to be set in {@code applicationContext.xml}
*/
public void setCluster(HzCluster cluster) {
this.cluster = cluster;
}
@Override
public void afterPropertiesSet() throws Exception {
Preconditions.checkNotNull(cluster, "HzCluster is not set");
}
/**
* Returns a {@link java.util.concurrent.locks.Lock} decorator for a Hazelcast {@link ILock
* distributed lock}
* <p>
* {@code path} is used as the key to acquire the distributed lock from
* {@link HazelcastInstance#getLock(String)}.
*
* @see org.geoserver.platform.resource.LockProvider#acquire(java.lang.String)
*/
@Override
public Lock acquire(final String path) {
Preconditions
.checkState(cluster.isEnabled(),
"Hazelcast cluster is not enabled. Either enable it or chose a different lock provider.");
Preconditions.checkState(cluster.isRunning(), "Hazelcast cluster is not running");
HazelcastInstance clusterInstance = cluster.getHz();
// if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.info(format("%s - Acquiring distributed lock '%s' (Thread %s)", nodeId(cluster),
path, Thread.currentThread().getName()));
// }
ILock lock = clusterInstance.getLock(path);
lock.lock();
// if (LOGGER.isLoggable(Level.FINE)) {
LOGGER.info(format("%s - Successfully acquired distributed lock '%s' (Thread %s)",
nodeId(cluster), path, Thread.currentThread().getName()));
// }
Preconditions.checkState(lock.isLockedByCurrentThread());
return new LockAdapter(lock, path, cluster);
}
private static class LockAdapter implements Lock {
private ILock lock;
private String path;
private HzCluster cluster;
public LockAdapter(ILock lock, String path, HzCluster cluster) {
this.lock = lock;
this.path = path;
this.cluster = cluster;
}
@Override
public void release() {
if (null == lock || !lock.isLocked()) {
LOGGER.info(format("%s - Distributed lock is already unlocked '%s' (Thread %s)",
nodeId(cluster), path, Thread.currentThread().getName()));
return;
}
try {
LOGGER.info(format("%s - Releasing distributed lock '%s' (Thread %s)",
nodeId(cluster), path, Thread.currentThread().getName()));
this.lock.unlock();
this.lock = null;
LOGGER.info(format("%s - Successfully released distributed lock '%s' (Thread %s)",
nodeId(cluster), path, Thread.currentThread().getName()));
} catch (RuntimeException e) {
LOGGER.log(
Level.SEVERE,
format("%s - Error releasing distributed lock '%s' (Thread %s): %s",
nodeId(cluster), path, Thread.currentThread().getName(),
e.getMessage()), e);
throw e;
}
}
}
}