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

com.netflix.conductor.postgres.dao.PostgresBaseDAO Maven / Gradle / Ivy

There is a newer version: 3.15.0
Show newest version
/*
 * 

* 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 com.netflix.conductor.postgres.dao; import java.io.IOException; import java.sql.Connection; import java.sql.SQLException; import java.time.Duration; import java.time.Instant; import java.util.Arrays; import java.util.List; import java.util.function.Consumer; import javax.sql.DataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.retry.support.RetryTemplate; import com.netflix.conductor.core.exception.NonTransientException; import com.netflix.conductor.postgres.util.*; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableList; public abstract class PostgresBaseDAO { private static final List EXCLUDED_STACKTRACE_CLASS = ImmutableList.of(PostgresBaseDAO.class.getName(), Thread.class.getName()); protected final Logger logger = LoggerFactory.getLogger(getClass()); protected final ObjectMapper objectMapper; protected final DataSource dataSource; private final RetryTemplate retryTemplate; protected PostgresBaseDAO( RetryTemplate retryTemplate, ObjectMapper objectMapper, DataSource dataSource) { this.retryTemplate = retryTemplate; this.objectMapper = objectMapper; this.dataSource = dataSource; } protected final LazyToString getCallingMethod() { return new LazyToString( () -> Arrays.stream(Thread.currentThread().getStackTrace()) .filter( ste -> !EXCLUDED_STACKTRACE_CLASS.contains( ste.getClassName())) .findFirst() .map(StackTraceElement::getMethodName) .orElseThrow(() -> new NullPointerException("Cannot find Caller"))); } protected String toJson(Object value) { try { return objectMapper.writeValueAsString(value); } catch (JsonProcessingException ex) { throw new NonTransientException(ex.getMessage(), ex); } } protected T readValue(String json, Class tClass) { try { return objectMapper.readValue(json, tClass); } catch (IOException ex) { throw new NonTransientException(ex.getMessage(), ex); } } protected T readValue(String json, TypeReference typeReference) { try { return objectMapper.readValue(json, typeReference); } catch (IOException ex) { throw new NonTransientException(ex.getMessage(), ex); } } /** * Initialize a new transactional {@link Connection} from {@link #dataSource} and pass it to * {@literal function}. * *

Successful executions of {@literal function} will result in a commit and return of {@link * TransactionalFunction#apply(Connection)}. * *

If any {@link Throwable} thrown from {@code TransactionalFunction#apply(Connection)} will * result in a rollback of the transaction and will be wrapped in an {@link * NonTransientException} if it is not already one. * *

Generally this is used to wrap multiple {@link #execute(Connection, String, * ExecuteFunction)} or {@link #query(Connection, String, QueryFunction)} invocations that * produce some expected return value. * * @param function The function to apply with a new transactional {@link Connection} * @param The return type. * @return The result of {@code TransactionalFunction#apply(Connection)} * @throws NonTransientException If any errors occur. */ private R getWithTransaction(final TransactionalFunction function) { final Instant start = Instant.now(); LazyToString callingMethod = getCallingMethod(); logger.trace("{} : starting transaction", callingMethod); try (Connection tx = dataSource.getConnection()) { boolean previousAutoCommitMode = tx.getAutoCommit(); tx.setAutoCommit(false); try { R result = function.apply(tx); tx.commit(); return result; } catch (Throwable th) { tx.rollback(); if (th instanceof NonTransientException) { throw th; } throw new NonTransientException(th.getMessage(), th); } finally { tx.setAutoCommit(previousAutoCommitMode); } } catch (SQLException ex) { throw new NonTransientException(ex.getMessage(), ex); } finally { logger.trace( "{} : took {}ms", callingMethod, Duration.between(start, Instant.now()).toMillis()); } } R getWithRetriedTransactions(final TransactionalFunction function) { try { return retryTemplate.execute(context -> getWithTransaction(function)); } catch (Exception e) { throw new NonTransientException(e.getMessage(), e); } } protected R getWithTransactionWithOutErrorPropagation(TransactionalFunction function) { Instant start = Instant.now(); LazyToString callingMethod = getCallingMethod(); logger.trace("{} : starting transaction", callingMethod); try (Connection tx = dataSource.getConnection()) { boolean previousAutoCommitMode = tx.getAutoCommit(); tx.setAutoCommit(false); try { R result = function.apply(tx); tx.commit(); return result; } catch (Throwable th) { tx.rollback(); logger.info(th.getMessage()); return null; } finally { tx.setAutoCommit(previousAutoCommitMode); } } catch (SQLException ex) { throw new NonTransientException(ex.getMessage(), ex); } finally { logger.trace( "{} : took {}ms", callingMethod, Duration.between(start, Instant.now()).toMillis()); } } /** * Wraps {@link #getWithRetriedTransactions(TransactionalFunction)} with no return value. * *

Generally this is used to wrap multiple {@link #execute(Connection, String, * ExecuteFunction)} or {@link #query(Connection, String, QueryFunction)} invocations that * produce no expected return value. * * @param consumer The {@link Consumer} callback to pass a transactional {@link Connection} to. * @throws NonTransientException If any errors occur. * @see #getWithRetriedTransactions(TransactionalFunction) */ protected void withTransaction(Consumer consumer) { getWithRetriedTransactions( connection -> { consumer.accept(connection); return null; }); } /** * Initiate a new transaction and execute a {@link Query} within that context, then return the * results of {@literal function}. * * @param query The query string to prepare. * @param function The functional callback to pass a {@link Query} to. * @param The expected return type of {@literal function}. * @return The results of applying {@literal function}. */ protected R queryWithTransaction(String query, QueryFunction function) { return getWithRetriedTransactions(tx -> query(tx, query, function)); } /** * Execute a {@link Query} within the context of a given transaction and return the results of * {@literal function}. * * @param tx The transactional {@link Connection} to use. * @param query The query string to prepare. * @param function The functional callback to pass a {@link Query} to. * @param The expected return type of {@literal function}. * @return The results of applying {@literal function}. */ protected R query(Connection tx, String query, QueryFunction function) { try (Query q = new Query(objectMapper, tx, query)) { return function.apply(q); } catch (SQLException ex) { throw new NonTransientException(ex.getMessage(), ex); } } /** * Execute a statement with no expected return value within a given transaction. * * @param tx The transactional {@link Connection} to use. * @param query The query string to prepare. * @param function The functional callback to pass a {@link Query} to. */ protected void execute(Connection tx, String query, ExecuteFunction function) { try (Query q = new Query(objectMapper, tx, query)) { function.apply(q); } catch (SQLException ex) { throw new NonTransientException(ex.getMessage(), ex); } } /** * Instantiates a new transactional connection and invokes {@link #execute(Connection, String, * ExecuteFunction)} * * @param query The query string to prepare. * @param function The functional callback to pass a {@link Query} to. */ protected void executeWithTransaction(String query, ExecuteFunction function) { withTransaction(tx -> execute(tx, query, function)); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy