Java 调度 kettle 时,JNDI 数据源动态配置。

本贴最后更新于 1807 天前,其中的信息可能已经时移世改

重要更新:
这种方式根本就不能实现我的业务,都是上千个定时任务并发执行的,并发去修改文件,亏我想的出来,研究半天白折腾了

前一段时间由于 kettle 数据源的问题,写了一篇博客,连接如下:
Java 调度 Kettle 时使用 jndi 连接数据库

上一篇博客的内容,解决了通过 Java 调用 kettle 时,覆盖默认的 jndi 数据源配置文件地址,实现自己在项目里进行配置数据源的需求。

kettle 对于不同数据源的处理

在 kettle 中,在一个流程里是可以使用多种数据源的,比如 表输入 组件从 一个 mysql 表中获取数据,可以通过 表输出 组件输出到另一个 Oracle 数据库中。

但是,有一个一直让我很纠结的问题,就是我搞一个通用的 流程,比如:
image.png

上面的流程比较简单,实现简单的从 X 库 到 A 库的数据抽取,A 库是固定的,X 库是动态的,类型、连接信息都是动态的,

这里,分了三个分支来处理三种不同的数据源,建表部分由于 sql 语法不同,所以无论如何都是要三种数据源单独处理的。

但是下面 这三个,其实是完全一样的,但是 由于连接数据库不一样,所以还是得做成三个:
image.png

下面看一下 kettle 数据源连接的方式:

image.png

这是 kettle 新建数据源的窗口,可以看出来,数据源的连接信息是填写的,并且是可以动态使用变量的。

但是数据库驱动从左边选择,无法动态传值。

根据 http://blog.gitor.org/articles/2019/06/29/1561815612401 里面说明,可以使用 JNDI 数据源来解决动态传人驱动名的问题,

但是 JNDI 是读取的配置文件,就算配置多种数据源驱动,但是数据库连接信息如果存储在数据库之类的,也是没办法动态传入的。

设想

在 kettle 执行前,先会进行初始化,这期间就会初始化 JNDI 数据源,那么是否可以在初始化之前 对 jndi 的 jdbc.properties 文件进行修改,然后 kettle 他还是自己去读这个文件呢?

实践

首先,由于初始化的时候就会将配置文件的路径初始化到 kettle 的全局变量中,所以需要在这个地方开始做文章。

kettle 初始化方法:

KettleEnvironment.init(false);

这个方法 默认一个 boolean 类型的参数,用于判断是否要在此时初始化 jndi 数据源,在另一篇文章中已有说明,他默认是 true 的,这里我给其传入 false ,阻止他在此时初始化,将初始化的方法自己重写。

kettle 默认的初始化方法:

public static void initJNDI() throws KettleException {
    String path = Const.JNDI_DIRECTORY;

    if ( path == null || path.equals( "" ) ) {
      try {
        File file = new File( "simple-jndi" );
        path = file.getCanonicalPath();
      } catch ( Exception e ) {
        throw new KettleException( "Error initializing JNDI", e );
      }
      Const.JNDI_DIRECTORY = path;
    }

    System.setProperty( "java.naming.factory.initial", "org.osjava.sj.SimpleContextFactory" );
    System.setProperty( "org.osjava.sj.root", path );
    System.setProperty( "org.osjava.sj.delimiter", "/" );
  }

其实可以看出来,核心的一行代码其实是:

System.setProperty( "org.osjava.sj.root", path );

我猜,在 kettle 运行的时候,就是去获取了 org.osjava.sj.root 的值来获取数据源的,

那么,既然我阻止了他自己初始化 jndi,而我又要使用 jndi ,就要自己去初始化了:

/**
     * 初始化 jndi 数据源
     * @param kettleParams 参数
     */
    static void initJNDI(KettleParams kettleParams) {

        logger.info("正在初始化jndi数据源……");
        try {
            Properties properties = new JndiProperRead().properties;

            OutputStream fos = new FileOutputStream(JndiProperRead.iniPath + "jdbc.properties");

            // 为避免自动转义,不用Properties自带的store方法。
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fos, StandardCharsets.UTF_8));

            // 先读取原本配置的ODS层固定数据源
            bw.write("###########ODS JNDI数据源############");
            bw.newLine();
            for(Enumeration<?> e = properties.keys(); e.hasMoreElements();) {
                String key = (String)e.nextElement();
                String val = properties.getProperty(key);
                bw.write(key + "=" + val);
                bw.newLine();
            }
            bw.newLine();
            // 动态配置SRC 数据源信息。
            bw.write("###########SRC JNDI数据源############");
            bw.newLine();
            bw.write("SRC/type="+getDbType(kettleParams.getDbType()));
            bw.newLine();
            bw.write("SRC/driver=oracle.jdbc.driver.OracleDriver");
            bw.newLine();
            bw.write("SRC/url=oracle.jdbc.driver.OracleDriver");
            bw.newLine();
            bw.write("SRC/user=oracle.jdbc.driver.OracleDriver");
            bw.newLine();
            bw.write("SRC/password=oracle.jdbc.driver.OracleDriver");
            bw.flush();

            //初始化kettle jndi 数据源。
            System.setProperty( "java.naming.factory.initial", "org.osjava.sj.SimpleContextFactory" );
            System.setProperty( "org.osjava.sj.root", JndiProperRead.iniPath.replace("jdbc.properties","") );
            System.setProperty( "org.osjava.sj.delimiter", "/" );
        }catch (Exception e){
            e.printStackTrace();
        }
    }

在这个自定义的初始化方法中,我读取了配置文件,通过修改了配置文件的内容以后,重新保存的。

kettle 读取的时候就可以读取到最新的配置文件了。

说明

这里面我在修改、保存 properties 文件的时候,没有使用 Properties 类,而是直接以文件流的形式修改的,因为用 Properties 的 store 保存以后,我发现,“:” 会被添加转义符号,但是读取的时候又不会去掉转义符号,会导致数据库连接不上的问题。

  • Kettle

    Kettle 是一款国外开源的 ETL 工具,纯 java 编写,可以在 Windows、Linux、Unix 上运行,数据抽取高效稳定。
    Kettle 中文名称叫水壶,该项目的主程序员 MATT 希望把各种数据放到一个壶里,然后以一种指定的格式流出。

    7 引用 • 13 回帖 • 1 关注
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3169 引用 • 8207 回帖
  • 数据治理
    1 引用 • 2 回帖
  • JNDI
    3 引用 • 7 回帖
1 操作
someone9891 在 2019-07-19 11:25:24 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...