protected AuditActionContext getAuditActionContext(final ResultSet resultSet) throws SQLException {
        val principal = resultSet.getString(AuditTableColumns.USER.getColumnName());
        val resource = resultSet.getString(AuditTableColumns.RESOURCE.getColumnName());
        val clientIp = resultSet.getString(AuditTableColumns.CLIENT_IP.getColumnName());
        val serverIp = resultSet.getString(AuditTableColumns.SERVER_IP.getColumnName());
        val audDate = resultSet.getTimestamp(AuditTableColumns.DATE.getColumnName());
        val appCode = resultSet.getString(AuditTableColumns.APPLIC_CD.getColumnName());
        val action = resultSet.getString(AuditTableColumns.ACTION.getColumnName());
        val userAgent = resultSet.getString(AuditTableColumns.USERAGENT.getColumnName());
        val tenant = resultSet.getString(AuditTableColumns.TENANT.getColumnName());
        val geoLocation = resultSet.getString(AuditTableColumns.GEOLOCATION.getColumnName());
        val locale = StringUtils.defaultIfBlank(resultSet.getString(AuditTableColumns.LOCALE.getColumnName()), Locale.US.toLanguageTag());

        val headers = new HashMap<String, String>();
        for (val headerName : headerNames) {
            val headerValue = resultSet.getString(headerName);
            if (StringUtils.isNotBlank(headerValue)) {
                headers.put(headerName, headerValue);
            }
        }
        val clientInfo = new ClientInfo(clientIp, serverIp, userAgent, geoLocation)
            .setLocale(Locale.forLanguageTag(locale))
            .setTenant(tenant)
            .setHeaders(headers);
        val auditDate = audDate.toLocalDateTime();
        return new AuditActionContext(principal, resource, action, appCode, auditDate, clientInfo);
    }
Instance of TransactionTemplate to manually execute a transaction since
threads are not in the same transaction.
support/cas-server-support-audit-jdbc/src/main/java/org/apereo/inspektr/audit/support/JdbcAuditTrailManager.java