/**
 * Copyright 2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.javacrumbs.shedlock.provider.jdbctemplate;

import net.javacrumbs.shedlock.core.LockConfiguration;
import net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider.Configuration;
import net.javacrumbs.shedlock.support.AbstractStorageAccessor;
import net.javacrumbs.shedlock.support.annotation.NonNull;
import org.springframework.dao.CannotSerializeTransactionException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.jdbc.UncategorizedSQLException;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.TransactionSystemException;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.Map;

import static java.util.Objects.requireNonNull;

/**
 * Spring JdbcTemplate based implementation usable in JTA environment
 */
class JdbcTemplateStorageAccessor extends AbstractStorageAccessor {
    private final NamedParameterJdbcTemplate jdbcTemplate;
    private final TransactionTemplate transactionTemplate;
    private final Configuration configuration;
    private SqlStatementsSource sqlStatementsSource;

    JdbcTemplateStorageAccessor(@NonNull Configuration configuration) {
        requireNonNull(configuration, "configuration can not be null");
        this.jdbcTemplate = new NamedParameterJdbcTemplate(configuration.getJdbcTemplate());
        this.configuration = configuration;
        PlatformTransactionManager transactionManager = configuration.getTransactionManager() != null ?
            configuration.getTransactionManager() :
            new DataSourceTransactionManager(configuration.getJdbcTemplate().getDataSource());

        this.transactionTemplate = new TransactionTemplate(transactionManager);
        this.transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
    }

    @Override
    public boolean insertRecord(@NonNull LockConfiguration lockConfiguration) {
        try {
            String sql = sqlStatementsSource().getInsertStatement();
            return transactionTemplate.execute(status -> {
                Map<String, Object> params = params(lockConfiguration);
                int insertedRows = jdbcTemplate.update(sql, params);
                return insertedRows > 0;
            });
        } catch (DuplicateKeyException | CannotSerializeTransactionException e) {
            return false;
        } catch (DataIntegrityViolationException | BadSqlGrammarException | UncategorizedSQLException | TransactionSystemException e) {
            logger.error("Unexpected exception", e);
            return false;
        }
    }

    @Override
    public boolean updateRecord(@NonNull LockConfiguration lockConfiguration) {
        String sql = sqlStatementsSource().getUpdateStatement();
        try {
            return transactionTemplate.execute(status -> {
                int updatedRows = jdbcTemplate.update(sql, params(lockConfiguration));
                return updatedRows > 0;
            });
        } catch (CannotSerializeTransactionException e) {
            return false;
        } catch (DataIntegrityViolationException | TransactionSystemException e) {
            logger.error("Unexpected exception", e);
            return false;
        }
    }

    @Override
    public boolean extend(@NonNull LockConfiguration lockConfiguration) {
        String sql = sqlStatementsSource().getExtendStatement();

        logger.debug("Extending lock={} until={}", lockConfiguration.getName(), lockConfiguration.getLockAtMostUntil());
        return transactionTemplate.execute(status -> {
            int updatedRows = jdbcTemplate.update(sql, params(lockConfiguration));
            return updatedRows > 0;
        });
    }

    @Override
    public void unlock(@NonNull LockConfiguration lockConfiguration) {
        try {
            doUnlock(lockConfiguration);
        } catch (TransactionSystemException e) {
            logger.info("Unlock failed due to TransactionSystemException - retrying");
            doUnlock(lockConfiguration);
        }
    }

    private void doUnlock(LockConfiguration lockConfiguration) {
        String sql = sqlStatementsSource().getUnlockStatement();
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(@NonNull TransactionStatus status) {
                jdbcTemplate.update(sql, params(lockConfiguration));
            }
        });
    }

    @NonNull
    private Map<String, Object> params(@NonNull LockConfiguration lockConfiguration) {
        return sqlStatementsSource().params(lockConfiguration);
    }

    private SqlStatementsSource sqlStatementsSource() {
        synchronized (configuration) {
            if (sqlStatementsSource == null) {
                sqlStatementsSource = SqlStatementsSource.create(configuration);
            }
            return sqlStatementsSource;
        }
    }
}

@@ -1566,22 +1566,25 @@
 nsaction
-Status
+Exception
 ;%0Aimport
@@ -1648,89 +1648,8 @@
 on;%0A
-import org.springframework.transaction.support.TransactionCallbackWithoutResult;%0A
 impo
@@ -3075,243 +3075,38 @@
 urn 
-transactionTemplate.execute(status -%3E %7B%0A                Map%3CString, Object%3E params = params(lockConfiguration);%0A                int insertedRows = jdbcTemplate.update(sql, params);%0A                return insertedRows %3E 0;%0A            %7D
+execute(sql, lockConfiguration
 );%0A 
@@ -3640,188 +3640,38 @@
 urn 
-transactionTemplate.execute(status -%3E %7B%0A                int updatedRows = jdbcTemplate.update(sql, params(lockConfiguration));%0A                return updatedRows %3E 0;%0A            %7D
+execute(sql, lockConfiguration
 );%0A 
@@ -4226,176 +4226,38 @@
 urn 
-transactionTemplate.execute(status -%3E %7B%0A            int updatedRows = jdbcTemplate.update(sql, params(lockConfiguration));%0A            return updatedRows %3E 0;%0A        %7D
+execute(sql, lockConfiguration
 );%0A 
@@ -4738,207 +4738,251 @@
     
-transactionTemplate.execute(new TransactionCallbackWithoutResult() %7B
+execute(sql, lockConfiguration);
 %0A    
- 
+%7D%0A%0A
     
-   @Override%0A            prot
+@SuppressWarnings(%22ConstantConditions%22)%0A    private boolean ex
 ec
+u
 te
-d void doInTransactionWithoutResult(@NonNull TransactionStatus status) %7B%0A               
+(String sql, LockConfiguration lockConfiguration) throws TransactionException %7B%0A        return transactionTemplate.execute(status -%3E
  jdb
@@ -5033,33 +5033,12 @@
 on))
-;%0A            %7D%0A        %7D
+ %3E 0
 );%0A 

Simplify JdbcTemplateStorageAccessor