插件清单

Metabase插件JAR包含一个插件清单 - 一个名为metabase-plugin.yaml的顶级文件。当Metabase启动时,它会遍历插件目录中的每个JAR,并在每个JAR中查找清单。此清单告诉Metabase插件提供的内容以及如何初始化它。

示例清单

info:
  name: Metabase SQLite Driver
  version: 1.0.0-SNAPSHOT-3.25.2
  description: Allows Metabase to connect to SQLite databases.
contact-info:
  name: Toucan McBird
  address: [email protected]
driver:
  name: sqlite
  display-name: SQLite
  lazy-load: true
  parent: sql-jdbc
  connection-properties:
    - name: db
      display-name: Filename
      placeholder: /home/camsaul/toucan_sightings.sqlite
      required: true
init:
  - step: load-namespace
    namespace: metabase.driver.sqlite
  - step: register-jdbc-driver
    class: org.sqlite.JDBC

《驱动》部分告诉 Metabase 插件定义了一个名为 :sqlite 的驱动,其父类为 :sql-jdbc。Metabase 的插件系统使用这些详细信息来调用 driver/register!。插件还列出了驱动程序的显示名称和连接属性,Metabase 的插件系统使用这些信息来创建 driver/display-namedriver/connection-properties 的实现。

延迟加载

如上例中的 示例 所示,驱动被列为 lazy-load: true,这意味着虽然上述方法实现会在 Metabase 启动时创建,但 Metabase 不会初始化驱动,直到有人第一次尝试连接到使用该驱动的数据库。

您可以(但不应该)将驱动设置为 lazy-load: false,这将使 Metabase 启动时间更长并消耗更多内存。

插件初始化

Metabase 会根据需要自动初始化插件。初始化过程大致如下:Metabase 将驱动程序添加到类路径中,然后按照顺序执行插件清单中的每个 init 部分。在上面的 示例清单 中,有两个步骤,一个是 load-namespace 步骤,另一个是 register-jdbc-driver 步骤。

init:
  - step: load-namespace
    namespace: metabase.driver.sqlite
  - step: register-jdbc-driver
    class: org.sqlite.JDBC

加载命名空间

您需要在您的驱动程序清单中添加一个或多个 load-namespace 步骤,以告诉 Metabase 哪些命名空间包含您的驱动程序方法实现。在上面的示例中,命名空间是 metabase.driver.sqlite。《load-namespace》调用 require 的常规 Clojure 方法,这意味着它会根据需要加载命名空间声明中 :require 部分列出的其他命名空间。如果您的驱动程序的方法实现分散在多个命名空间中,请确保它们也会被加载——您可以通过在命名空间声明中的 :require 表达式包括它们或通过添加额外的 load-namespace 步骤来处理。

有关命名空间的更多信息,请参阅 Clojure 命名空间

注册 JDBC 驱动程序

底层使用 JDBC 驱动程序的驱动程序还需要添加一个 register-jdbc-driver 步骤。

如果您对此感兴趣,原因在于 Java 的 JDBC DriverManager 不会使用除系统 ClassLoader 之外加载的 JDBC 驱动程序,这实际上意味着 Drivermanager 只会使用作为 Metabase 核心uberjar 部分打包的 JDBC 驱动程序类。由于系统类加载器不允许在运行时加载类路径,Metabase 使用自定义的 ClassLoader 来初始化插件。为了克服这一限制,Metabase 随附一个 JDBC 代理驱动程序类,它可以包装其他 JDBC 驱动程序。当 Metabase 调用 register-jdbc-driver 时,Metabase 实际上注册了一个代理类的实例,该方法调用将转发到实际的 JDBC 驱动程序。这完全符合 DriverManager 的要求。

构建驱动程序

要构建作为插件 JAR 的驱动程序,请参阅 构建驱动脚本 README

将您构建的 JAR 文件放置在 Metabase 的 /plugins 目录中,然后您就可以开始使用了。

Metabase插件清单参考

以下是一个带有注释的示例插件清单,可以帮助您开始编写自己的插件。

# Basic user-facing information about the driver goes under the info: key
info:

  # Make sure to give your plugin a name. In the future, we can show
  # this to the user in a 'plugin management' admin page.
  name: Metabase SQLite Driver

  # For the sake of consistency with the core Metabase project you
  # should use semantic versioning. It's not a bad idea to include the
  # version of its major dependency (e.g., a JDBC driver) when
  # applicable as part of the 'patch' part of the version, so we can
  # update dependencies and have that reflected in the version number
  #
  # For now core Metabase modules should have a version
  # 1.0.0-SNAPSHOT-x until version 1.0 ships and the API for plugins
  # is locked in
  version: 1.0.0-SNAPSHOT-3.25.2

  # Describe what your plugin does. Not used currently, but in the
  # future we may use this description in a plugins admin page.
  description: Allows Metabase to connect to SQLite databases.

# You can list any dependencies needed by the plugin by specifying a
# list of dependencies. If all dependencies are not met, the plugin
# will not be initialized.
#
# A dependency may be either a 'class' or (in the future) a 'plugin' dependency
dependencies:

  # A 'class' dependency checks whether a given class is available on
  # the classpath. It doesn't initialize the class; Metabase defers initialization
  # until it needs to use the driver
  # Don't use this for classes that ship as part of the plugin itself;
  # only use it for external dependencies.
  - class: oracle.jdbc.OracleDriver

    # You may optionally add a message that will be displayed for
    # information purposes in the logs, and possibly in a plugin
    # management page as well in the future
    message: >
      Metabase requires the Oracle JDBC driver to connect to JDBC databases. See
      https://metabase.net.cn/docs/latest/administration-guide/databases/oracle.html
      for more details

  # A 'plugin' dependency checks whether a given plugin is available.
  # The value for 'plugin' is whatever that plugin has as its 'name:' -- make
  # sure you match it exactly!
  #
  # If the dependency is not available when this module is first loaded, the module
  # will be tried again later after more modules are loaded. This means things will
  # still work the way we expect even if, say, we initially attempt to load the
  # BigQuery driver *before* loading its dependency, the shared Google driver. Once
  # the shared Google driver is loaded, Metabase will detect that BigQuery's
  # dependencies are now satisfied and initialize the plugin.
  #
  # In the future, we'll like add version restrictions as well, but for now we only match
  # by name.
  - plugin: Metabase SQLHeavy Driver

# If a plugin adds a driver it should define a driver: section.
#
# To define multiple drivers, you can pass a list of maps instead. Note
# that multiple drivers currently still must share the same dependencies
# set and initialization steps. Thus registering multiple drivers is most
# useful for slight variations on the same driver or including an abstract
# parent driver. Note that init steps will get ran once for each driver
# that gets loaded. This can result in duplicate driver instances registered
# with the DriverManager, which is certainly not ideal but does not actually
# hurt anything.
#
# In the near future I might move init steps into driver itself (or
# at least allow them there)
driver:

  # Name of the driver; corresponds to the keyword (e.g. :sqlite) used
  # in the codebase
  name: sqlite

  # Nice display name shown to admins when connecting a database
  display-name: SQLite

  # Whether loading this driver can be deferred until the first
  # attempt to connect to a database of this type. Default: true. Only
  # set this to false if absolutely neccesary.
  lazy-load: true

  # Parent driver, if any.
  parent: sql-jdbc

  # You may alternatively specify a list of parents for drivers with
  # more than one:
  parent:
    - google
    - sql

  # Whether this driver is abstract. Default: false
  abstract: false

  # List of connection properties to ask users to set to connect to
  # this driver.
  connection-properties:
    # Connection properties can be one of the defaults found in
    # metabase.driver.common, listed by name:
    - dbname
    - host

    # Or a full map for a custom option. Complete schema for custom
    # options can be found in metabase.driver. NOTE: these are not
    # currently translated for i18n; I'm working on a way to translate
    # these. Prefer using one of the defaults from
    # metabase.driver.common if possible.
    - name: db
      display-name: Filename
      placeholder: /home/camsaul/toucan_sightings.sqlite
      required: true

    # Finally, you can use merge: to merge multiple maps. This is
    # useful to override specific properties in one of the defaults.
    - merge:
      - port
      - placeholder: 1433

# Steps to take to initialize the plugin. For lazy-load drivers, this
# is delayed until the driver is initialized the first time we connect
# to a database with it
init:

  # load-namespace tells Metabase to require a namespace from the JAR,
  # you can do whatever Clojurey things you need to do inside that
  # namespace
  - step: load-namespace
    namespace: metabase.driver.sqlite

  # register-jdbc-driver tells Metabase to register a JDBC driver that
  # will be used by this driver. (It actually registers a proxy
  # driver, because DriverManager won't allow drivers that are loaded
  # by different classloaders than it was loaded by (i.e., the system
  # classloader); don't worry to much about this, but know for
  # JDBC-based drivers you need to include your dependency here)
  - step: register-jdbc-driver
    class: org.sqlite.JDBC

接下来

为您的驱动程序实现多方法

阅读其他Metabase版本的相关文档。

想改进这些文档? 提出更改。