插件清单
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-name
和 driver/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版本的相关文档。