All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.zalando.sprocwrapper.proxy.executors.ExecutorWrapper Maven / Gradle / Ivy

Go to download

Library to make PostgreSQL stored procedures available through simple Java "*SProcService" interfaces including automatic object serialization and deserialization (using typemapper and convention-over-configuration). Supports sharding, advisory locking, statement timeouts and PostgreSQL types such as enums and hstore.

There is a newer version: 2.0.0
Show newest version
package de.zalando.sprocwrapper.proxy.executors;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.zalando.sprocwrapper.SProcCall.AdvisoryLock;
import de.zalando.sprocwrapper.dsprovider.SameConnectionDatasource;
import de.zalando.sprocwrapper.proxy.InvocationContext;

/**
 * This Executor wraps stored procedure calls that use advisory locks and / or need different statement timeouts set.
 *
 * @author  jmussler
 */
public class ExecutorWrapper implements Executor {

    private final Executor executor;
    private final long timeoutInMilliSeconds;
    private final AdvisoryLock lock;

    private static final Logger LOG = LoggerFactory.getLogger(ExecutorWrapper.class);

    public ExecutorWrapper(final Executor e, final long t, final AdvisoryLock a) {
        executor = e;
        timeoutInMilliSeconds = t;
        lock = a;
    }

    private void setTimeout(final Connection conn) throws SQLException {
        if (timeoutInMilliSeconds <= 0) {
            return;
        }

        LOG.debug("Setting statement timeout {}", timeoutInMilliSeconds);

        final Statement st = conn.createStatement();
        st.execute("SET application_name TO 'timeout:" + timeoutInMilliSeconds + "'");
        st.execute("SET statement_timeout TO " + timeoutInMilliSeconds);
        st.close();
    }

    private void resetTimeout(final Connection conn) throws SQLException {
        if (timeoutInMilliSeconds <= 0) {
            return;
        }

        LOG.debug("Resetting statement timeout");

        final Statement st = conn.createStatement();
        st.execute("RESET statement_timeout");
        st.execute("RESET application_name");
        st.close();
    }

    private boolean lockAdvisoryLock(final Connection conn) throws SQLException {
        if (lock == null || lock.equals(AdvisoryLock.NoLock.LOCK)) {
            return true;
        }

        final Statement st = conn.createStatement();
        final ResultSet rs = st.executeQuery("SELECT pg_advisory_lock(" + lock.getLockId() + ") AS \"" + lock
                .getName() + "\";");

        boolean b = false;
        if (rs.next()) {
            b = true;
        }

        rs.close();
        st.close();
        return b;
    }

    private boolean unlockAdvisoryLock(final Connection conn) throws SQLException {
        if (lock == null || lock.equals(AdvisoryLock.NoLock.LOCK)) {
            return true;
        }

        final Statement st = conn.createStatement();
        final ResultSet rs = st.executeQuery("SELECT pg_advisory_unlock(" + lock.getLockId() + ")");
        boolean b = false;
        if (rs.next()) {
            b = rs.getBoolean(1);
        }

        rs.close();
        st.close();
        return b;
    }

    @Override
    public Object executeSProc(final DataSource ds, final String sql, final Object[] args, final int[] types,
                               final InvocationContext invocationContext, final Class returnType) {

        SameConnectionDatasource sameConnDs = null;

        try {

            sameConnDs = new SameConnectionDatasource(ds.getConnection());

            setTimeout(sameConnDs.getConnection());

            if (!lockAdvisoryLock(sameConnDs.getConnection())) {
                throw new RuntimeException("Could not acquire AdvisoryLock " + lock.getName());
            }

            return executor.executeSProc(sameConnDs, sql, args, types, invocationContext, returnType);

        } catch (final SQLException e) {

            throw new RuntimeException("SQL Exception in execute sproc: " + sql, e);

        } finally {
            if (sameConnDs != null) {
                try {

                    if (timeoutInMilliSeconds > 0) {
                        try {
                            resetTimeout(sameConnDs.getConnection());
                        } catch (final SQLException ex) {
                            LOG.error("Exception in reseting statement timeout!", ex);
                        }
                    }

                    // unlock in all cases, locks not owned by this session cannot be unlocked
                    if (lock != null && !lock.equals(AdvisoryLock.NoLock.LOCK)) {
                        try {
                            unlockAdvisoryLock(sameConnDs.getConnection());
                        } catch (final SQLException ex) {
                            LOG.error("Exception in reseting advisory lock!", ex);
                        }
                    }
                } finally {
                    try {
                        sameConnDs.close();
                    } catch (final SQLException ex) {
                        LOG.error("Exception in closing underlying connection!", ex);
                    }
                }
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy