// Path: intf/src/main/java/funcatron/intf/Accumulator.java
// public interface Accumulator {
//     /**
//      * Accumulate vendors that need "finishing"
//      * @param item the item that was vended
//      * @param vendor the thing doing the vending
//      * @param <T> the type of thing
//      */
//     <T> void accumulate(T item, ServiceVendor<T> vendor);
// 
//     /**
//      * For all the vended things, finish them either successfully onsuccessfully.
//      *
//      * @param success was the operation a success (e.g., commit) or not (e.g., rollback)
//      */
//     void finished(boolean success);
// }
// 
// Path: intf/src/main/java/funcatron/intf/ServiceVendor.java
// public interface ServiceVendor<T> {
// 
//     /**
//      * The name of the service
//      * @return the name of the service
//      */
//     String name();
// 
//     /**
//      * The type of the service vended
//      *
//      * @return the class object of the type of service vended
//      */
//     Class<T> type();
// 
// 
//     /**
//      * Is the class of a given type? For example `ofType(java.sql.Connection.class)`
//      * to test for JDBC
//      * @param clz the class to test the instance against
//      * @return boolean
//      */
//     default boolean ofType(Class<?> clz) {
//         return type().isAssignableFrom(clz);
//     }
// 
//     /**
//      * Vends an instance of the thing (like a JDBC connection)
//      *
//      * @param acc the accumulator
//      * @return an instance of the thing
//      * @throws Exception failure to create the instance
//      */
//     T vend(Accumulator acc) throws Exception;
// 
//     /**
//      * Close down any resources the vendor has (e.g., close all the JDBC connections)
//      */
//     void endLife();
// 
//     /**
//      * Release the resource at the end of a Func execution. Depending on `success`,
//      * the resource may be released in different ways (e.g., database commit vs. database rollback)
//      * @param item the item to release
//      * @param success true if the function executed successfully and the transaction should be committed. false on an exception
//      * @throws Exception if the release operation throws an exception
//      */
//     void release(T item, boolean success) throws Exception;
// }
// 
// Path: intf/src/main/java/funcatron/intf/ServiceVendorProvider.java
// public interface ServiceVendorProvider {
//     /**
//      * The string type that this Builder will build a vendor for.
//      * Example "database"
//      * @return the name of the type this builder will build for
//      */
//     String forType();
// 
// 
//     /**
//      * If the 'type' field in the properties matches
//      * @param name the name of the ServiceVendor
//      * @param properties properties for the service vendor... for example the JDBC connection information
//      * @param logger log anything to the logger
//      * @return If we can successfull build the service vendor, build it
//      */
//     Optional<ServiceVendor<?>> buildVendor(String name, Map<String, Object> properties, Logger logger);
// }


import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import funcatron.intf.Accumulator;
import funcatron.intf.ServiceVendor;
import funcatron.intf.ServiceVendorProvider;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
     * @param name       the name of the ServiceVendor
     * @param properties properties for the service vendor... for example the JDBC connection information
     * @param logger     log anything to the logger
     * @return If we can successfull build the service vendor, build it
     */
    @Override
    public Optional<ServiceVendor<?>> buildVendor(String name, Map<String, Object> properties, Logger logger) {
        Set<String> reserved = new HashSet<>();
        String[] ra = {"type", "url", "username", "password"};
        Collections.addAll(reserved, ra);
        Object url = properties.get("url");
        Object username = properties.get("username");
        Object password = properties.get("password");
        Object classname = properties.get("classname");

        if (null != url && url instanceof String) {
            if (null != classname && classname instanceof String) {
                try {
                    Class<?> clz = this.getClass().getClassLoader().loadClass((String) classname);
                    logger.log(Level.INFO, () -> "Loaded class " + clz +" for JDBC");
                    DriverManager.registerDriver((Driver) clz.newInstance());
                } catch (Exception cnf) {
                    logger.log(Level.WARNING, cnf, () -> "Unable to load DB driver class "+classname);
                }
            }

            HikariConfig config = new HikariConfig();
            config.setJdbcUrl((String) url);
            if (null != username && username instanceof String) config.setUsername((String) username);
            if (null != password && password instanceof String) config.setPassword((String) password);
// Path: intf/src/main/java/funcatron/intf/Accumulator.java
// public interface Accumulator {
//     /**
//      * Accumulate vendors that need "finishing"
//      * @param item the item that was vended
//      * @param vendor the thing doing the vending
//      * @param <T> the type of thing
//      */
//     <T> void accumulate(T item, ServiceVendor<T> vendor);
// 
//     /**
//      * For all the vended things, finish them either successfully onsuccessfully.
//      *
//      * @param success was the operation a success (e.g., commit) or not (e.g., rollback)
//      */
//     void finished(boolean success);
// }
// 
// Path: intf/src/main/java/funcatron/intf/ServiceVendor.java
// public interface ServiceVendor<T> {
// 
//     /**
//      * The name of the service
//      * @return the name of the service
//      */
//     String name();
// 
//     /**
//      * The type of the service vended
//      *
//      * @return the class object of the type of service vended
//      */
//     Class<T> type();
// 
// 
//     /**
//      * Is the class of a given type? For example `ofType(java.sql.Connection.class)`
//      * to test for JDBC
//      * @param clz the class to test the instance against
//      * @return boolean
//      */
//     default boolean ofType(Class<?> clz) {
//         return type().isAssignableFrom(clz);
//     }
// 
//     /**
//      * Vends an instance of the thing (like a JDBC connection)
//      *
//      * @param acc the accumulator
//      * @return an instance of the thing
//      * @throws Exception failure to create the instance
//      */
//     T vend(Accumulator acc) throws Exception;
// 
//     /**
//      * Close down any resources the vendor has (e.g., close all the JDBC connections)
//      */
//     void endLife();
// 
//     /**
//      * Release the resource at the end of a Func execution. Depending on `success`,
//      * the resource may be released in different ways (e.g., database commit vs. database rollback)
//      * @param item the item to release
//      * @param success true if the function executed successfully and the transaction should be committed. false on an exception
//      * @throws Exception if the release operation throws an exception
//      */
//     void release(T item, boolean success) throws Exception;
// }
// 
// Path: intf/src/main/java/funcatron/intf/ServiceVendorProvider.java
// public interface ServiceVendorProvider {
//     /**
//      * The string type that this Builder will build a vendor for.
//      * Example "database"
//      * @return the name of the type this builder will build for
//      */
//     String forType();
// 
// 
//     /**
//      * If the 'type' field in the properties matches
//      * @param name the name of the ServiceVendor
//      * @param properties properties for the service vendor... for example the JDBC connection information
//      * @param logger log anything to the logger
//      * @return If we can successfull build the service vendor, build it
//      */
//     Optional<ServiceVendor<?>> buildVendor(String name, Map<String, Object> properties, Logger logger);
// }


// Path: intf/src/main/java/funcatron/intf/impl/JDBCServiceVendorBuilder.java
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import funcatron.intf.Accumulator;
import funcatron.intf.ServiceVendor;
import funcatron.intf.ServiceVendorProvider;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

     * @param name       the name of the ServiceVendor
     * @param properties properties for the service vendor... for example the JDBC connection information
     * @param logger     log anything to the logger
     * @return If we can successfull build the service vendor, build it
     */
    @Override
    public Optional<ServiceVendor<?>> buildVendor(String name, Map<String, Object> properties, Logger logger) {
        Set<String> reserved = new HashSet<>();
        String[] ra = {"type", "url", "username", "password"};
        Collections.addAll(reserved, ra);
        Object url = properties.get("url");
        Object username = properties.get("username");
        Object password = properties.get("password");
        Object classname = properties.get("classname");

        if (null != url && url instanceof String) {
            if (null != classname && classname instanceof String) {
                try {
                    Class<?> clz = this.getClass().getClassLoader().loadClass((String) classname);
                    logger.log(Level.INFO, () -> "Loaded class " + clz +" for JDBC");
                    DriverManager.registerDriver((Driver) clz.newInstance());
                } catch (Exception cnf) {
                    logger.log(Level.WARNING, cnf, () -> "Unable to load DB driver class "+classname);
                }
            }

            HikariConfig config = new HikariConfig();
            config.setJdbcUrl((String) url);
            if (null != username && username instanceof String) config.setUsername((String) username);
            if (null != password && password instanceof String) config.setPassword((String) password);
            properties.entrySet().stream().