package org.activityinfo.server.endpoint.gwtrpc;
/*
* #%L
* ActivityInfo Server
* %%
* Copyright (C) 2009 - 2013 UNICEF
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #L%
*/
import com.google.common.base.Preconditions;
import com.google.common.base.Stopwatch;
import org.activityinfo.legacy.shared.exception.CommandTimeoutException;
import org.activityinfo.legacy.shared.exception.UnexpectedCommandException;
import org.hibernate.Query;
import org.hibernate.ejb.HibernateEntityManager;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* @author yuriyz on 10/10/2014.
*/
public class AdvisoryLock implements AutoCloseable {
private static final Logger LOGGER = Logger.getLogger(AdvisoryLock.class.getName());
public static final String ADVISORY_LOCK_NAME = "activityinfo.remote_execution_context";
public static final int ADVISORY_GET_LOCK_TIMEOUT = 10;
public static final int SUCCESS_CODE = 1;
private static final int TIMEOUT_CODE = 0;
private final HibernateEntityManager entityManager;
public AdvisoryLock(HibernateEntityManager entityManager) {
Preconditions.checkNotNull(entityManager);
this.entityManager = entityManager;
try {
Stopwatch stopwatch = Stopwatch.createStarted();
String sql = String.format("SELECT GET_LOCK('%s', %s)", ADVISORY_LOCK_NAME, ADVISORY_GET_LOCK_TIMEOUT);
Query query = entityManager.getSession().createSQLQuery(sql);
Object result = query.uniqueResult();
if (result == null) {
throw new UnexpectedCommandException("Error occurred while trying to obtain advisory lock.");
}
int resultCode = ((Number) result).intValue();
if (resultCode == TIMEOUT_CODE) { // time out
LOGGER.severe("Timed out waiting for write lock.");
throw new CommandTimeoutException();
}
if (resultCode != SUCCESS_CODE) { // not success
LOGGER.severe("Failed to acquire lock, result code: " + resultCode);
throw new RuntimeException("Failed to acquire lock, result code: " + resultCode);
}
stopwatch.stop();
LOGGER.finest("Acquiring advisory lock takes: " + stopwatch.elapsed(TimeUnit.MILLISECONDS) + "ms");
} catch (CommandTimeoutException e) {
throw e;
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Internal error during acquiring advisory lock: " + e.getMessage(), e);
throw new RuntimeException("Exception caught while trying to acquire update lock", e);
}
}
@Override
public void close() throws Exception {
Stopwatch stopwatch = Stopwatch.createStarted();
String sql = String.format("SELECT RELEASE_LOCK('%s')", ADVISORY_LOCK_NAME);
Query query = entityManager.getSession().createSQLQuery(sql);
Object result = query.uniqueResult();
int resultCode = ((Number) result).intValue();
if (resultCode != SUCCESS_CODE) {
throw new RuntimeException("Failed to release lock, result code: " + resultCode);
}
stopwatch.stop();
LOGGER.finest("Release lock takes: " + stopwatch.elapsed(TimeUnit.MILLISECONDS) + "ms");
}
public void closeQuietly() {
try {
close();
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Failed to release lock.", e);
}
}
public static void closeQuietly(AdvisoryLock lock) {
if (lock != null) {
lock.closeQuietly();
}
}
}