Kettle中如何正確存放插件和調試插件


調試遇到的問題

按照給定的源碼,外部插件是沒有加加載的。因為外部插件有兩種加載方式。一個是以jar包方式,一個是plugin.xml方式加裝。調試發現如果以jar形式加載,需要將jar包放到/ui/plugins目錄下(而不是源代碼目錄/plugins目錄)而這種方式導致有些插件pdi-xml-plugin插件加載會報錯Unexpected error loading class: General - org/pentaho/metaverse/api/analyzer/kettle/step/IStepExternalResourceConsumer
, 分析發現pdi-xml-plugin的pom.xml文件發現依賴pentaho-metaverse-api包,但是看了下Eclipse 中Maven Dependencies有這個包。我猜可能這種加載jar包找不到這個包(我也驗證過,將maven倉庫中的這個包拷貝到這個插件libs目錄下確實不報沒有這個錯,但是報其他包的錯。感覺拷貝maven包的方式肯定不行,而且其他插件也需要這個包也要拷貝,造成包重復,也違背了maven倉庫管理原則),如是要考慮其他方法。但是看了經過mvn命令行編譯好的.源代碼目錄pentaho-kettle-9.2.0.3-R\assemblies\client\target下的pdi-ce-9.2.0.3-477.zip解壓后,我們看到如下目錄
lib:所有maven倉庫的包
libswt:不同平台下的swt包(linux,osx64,win64)
plugins:所有外部插件
ui:都是些*.xul文件
launcher:里含有launcher.jar文件和launcher.properties文件而launcher.properties中指定了lib,libswt路徑


main=org.pentaho.di.ui.spoon.Spoon
libraries=../test:../lib:../libswt
classpath=../classes:../:../ui:../ui/images:../lib
system-property.pentaho.installed.licenses.file=${PENTAHO_INSTALLED_LICENSE_PATH}


在main方法中 KettleEnvironment.init()之前添加了

 System.setProperty("KETTLE_PLUGIN_BASE_FOLDERS","E:\\DevOps\\ETL\\pentaho-kettle-9.2.0.3-R\\plugins");

重新cd到源碼目錄,用如下命令編譯項目

mvn clean package

運行調試都報錯

Caused by: java.lang.NoClassDefFoundError: com/google/api/client/json/JsonFactory
	at org.pentaho.googledrive.lifecycle.GoogleDrivePluginLifecycleListener.onEnvironmentInit(GoogleDrivePluginLifecycleListener.java:49)
	at org.pentaho.di.core.lifecycle.KettleLifecycleSupport.onEnvironmentInit(KettleLifecycleSupport.java:116)
	at org.pentaho.di.core.lifecycle.KettleLifecycleSupport.onEnvironmentInit(KettleLifecycleSupport.java:107)
	at org.pentaho.di.core.KettleEnvironment.initLifecycleListeners(KettleEnvironment.java:184)
	at org.pentaho.di.core.KettleEnvironment.init(KettleEnvironment.java:157)

調試分析異常

經驗證去掉System.setProperty("KETTLE_PLUGIN_BASE_FOLDERS","E:\DevOps\ETL\pentaho-kettle-9.2.0.3-R\plugins");這句話打包程序運行就不會報錯。
如果不加入這句話,需要將plugins拷貝到pentaho-kettle-9.2.0.3-R\ui下,否則jar包就不會加載,本來將編譯好的plugins目錄及文件拷貝到pentaho-kettle-9.2.0.3-R\ui下調試,發現kettile-json-plugin-core插件報錯

General - ERROR (version Unknown) : Unexpected error loading class for plugin JsonOutputAnalyzerPlugin
General - ERROR (version Unknown) : org.pentaho.di.core.exception.KettlePluginException: 
General - Unexpected error loading class:
General - org/pentaho/metaverse/api/analyzer/kettle/step/IStepExternalResourceConsumer

猜測缺少包,但maven倉庫中有這個包,只是這個插件下lib下沒有這個包(而且這個不像內部插件調試環境有Maven Dependciies庫,外部插件雖然也有Maven Dependciies庫,但是以jar包形式加載這個時候還沒有進入調試環境,所有即使有Maven Dependciies庫也沒用,還必須各插件下lib目錄有所有的依賴包)
Spoon.bat指定了jre,launcher.jar
因為lib所有jar包都是公用的,所以可以加載外部的jar包插件不報錯.

遺留問題1
這里需要理解的是有些插件下面lib目錄有些依賴的jar包,有些沒有,有些不完整。

從Spoon.java啟動調試的不好地方

1.調試插件有些插件缺依賴的jar包,需要拷貝依賴包到編譯的插件下的lib目錄下
2.system下的karaf(osgi插件),比如編譯號的data-integration\system\karaf\system\org\pentaho\di\plugins下的file-open-save-plugin插件加載不了,導致內部插件Text file input插件中的【Browse】打不開對話框,經調試無法osgi的file-open-save-plugin插件
可以從/kettle-ui-swt/src/main/java/org/pentaho/di/ui/core/events/dialog/SelectionAdapterFileDialog.java中的widgetSelectedHelper方法中的

 extensionPointWrapper.callExtensionPoint( log, 
KettleExtensionPoint.SpoonOpenSaveNew.id, fileDialogOperation );

目前最佳的做法

將整個程序編譯打包后的代碼pentaho-kettle-9.2.0.3-R\assemblies\client\target下的pdi-ce-9.2.0.3-477.zip解壓,將plugins整個目錄拷貝到pentaho-kettle-9.2.0.3-R\ui目錄下,雖然有些插件報錯,但是插件都加載出來,而且可以調試,后續就是解決這些報錯問題。

public class PluginRegistryPluginType extends BasePluginType implements PluginTypeInterface {

  private static PluginRegistryPluginType INSTANCE = new PluginRegistryPluginType();

  public PluginRegistryPluginType() {
    super( RegistryPlugin.class, "Plugin Extensions", "Plugin Registry Extension Types" );
    populateFolders( "pluginRegistry" );
  }

  @Override
  protected String getXmlPluginFile() {
    // This property is set by the import-export.bat/sh command line script to disable this native resource file from
    // processing.
    if ( !Utils.isEmpty( getProperty( "pentaho.disable.karaf", "" ) ) ) {
      return "KettleFileDisabled.xmldisabledxyzabc";  //we return a file that does not exist to suppress exceptions.
    }
    return Const.XML_FILE_KETTLE_REGISTRY_EXTENSIONS;
  }

XML_FILE_KETTLE_REGISTRY_EXTENSIONS = "kettle-registry-extensions.xml"內容如下

<registry-extensions>

  <registry-extension id="PdiOsgiBridge">

    <description>PDI-OSGI-Bridge Extension</description>

    <tooltip/>    
<classname>org.pentaho.di.osgi.registryExtension.OSGIPluginRegistryExtension</classname>

    <meta-classname/>

    <version-browser-classname/>

  </registry-extension>
</registry-extensions>


注意
PluginRegistryExtension
ExtensionPointPluginType

項目中有兩種實現了PluginRegistryExtension接口類的

org.pentaho.di.osgi.registryExtension.OSGIPluginRegistryExtension
org.pentaho.di.bigdata.ShimDependentPluginRegistryPlugin
這里主要說明OSGIPluginRegistryExtension

PluginRegistry.java中registerType方法中

 for ( PluginRegistryExtension ext : extensions ) {

      ext.searchForType( pluginType );

    }


PluginRegistryExtension接口代碼如下

public interface PluginRegistryExtension {

  void init( PluginRegistry registry );
  void searchForType( PluginTypeInterface pluginType );
  String getPluginId( Class<? extends PluginTypeInterface> pluginType, Object 
pluginClass );

}


OSGI需另行研究
org.pentaho.di.osgi.OSGIPluginType
通過Everything工具高級搜索OSGIPluginRegistryExtension,找到了pdi-osgi-bridge-core-9.2.0.3-477.jar包,同時又去maven倉庫中找到了對應的源代碼包pdi-osgi-bridge-core-9.2.0.3-477-sources.jar解壓就可以看到OSGIPluginRegistryExtension.java代碼實現,同時包含了OsgPlugin及OsgPluginType實現

@RegistryPlugin(id = "OSGIRegistryPlugin", name = "OSGI")
public class OSGIPluginRegistryExtension implements PluginRegistryExtension {
  private static OSGIPluginRegistryExtension INSTANCE;
  private OSGIPluginTracker tracker = OSGIPluginTracker.getInstance();
  private Logger logger = LoggerFactory.getLogger( getClass() );
  private KarafBoot boot = new KarafBoot();
  private StatusGetter<Boolean> kettleClientEnvironmentInitialized = new StatusGetter<Boolean>() {
    @Override public Boolean get() {
      return KettleClientEnvironment.isInitialized();
    }
  };
  private AtomicBoolean initializedKaraf = new AtomicBoolean( false );

  public OSGIPluginRegistryExtension() {
    INSTANCE = this;
  }

  public static OSGIPluginRegistryExtension getInstance() {
    if ( INSTANCE == null ) {
      throw new IllegalStateException( "Kettle is supposed to construct this first" );
    }
    return INSTANCE;
  }

  // FOR UNIT TEST ONLY
  protected static void setInstance( OSGIPluginRegistryExtension instance ) {
    INSTANCE = instance;
  }

  // FOR UNIT TEST ONLY
  protected void setTracker( OSGIPluginTracker tracker ) {
    this.tracker = tracker;
  }

  // FOR UNIT TEST ONLY
  protected void setLogger( Logger logger ) {
    this.logger = logger;
  }


  // FOR UNIT TEST ONLY
  protected void setKettleClientEnvironmentInitialized( StatusGetter<Boolean> kettleClientEnvironmentInitialized ) {
    this.kettleClientEnvironmentInitialized = kettleClientEnvironmentInitialized;
  }

  @VisibleForTesting
  void setKarafBoot( KarafBoot boot ){
    this.boot = boot;
  }

  public KarafBoot getKarafBoot(){
    return boot;
  }

  public synchronized void init( final PluginRegistry registry ) {
    if ( PentahoSystem.getInitializedStatus() != PentahoSystem.SYSTEM_INITIALIZED_OK && !initializedKaraf.getAndSet( true )) {
      String userDir = System.getProperty("pentaho.user.dir", ".");
      IApplicationContext context = new StandaloneApplicationContext( userDir, userDir );
      PentahoSystem.init(context);
      boot.startup( null );
    }
    PluginRegistry.addPluginType( OSGIPluginType.getInstance() );
    tracker.registerPluginClass( PluginInterface.class );

    tracker.addPluginLifecycleListener( PluginInterface.class,
      new PluginRegistryOSGIServiceLifecycleListener( registry ) );
  }

  @Override
  public void searchForType( PluginTypeInterface pluginType ) {
    tracker.registerPluginClass( pluginType.getClass() );
  }

  @Override
  public String getPluginId( Class<? extends PluginTypeInterface> pluginType, Object pluginClass ) {
    try {
      return (String) tracker.getBeanPluginProperty( pluginType, pluginClass, "ID" );
    } catch ( Exception e ) {
      logger.error( e.getMessage(), e );
    }
    return null;
  }
}

參考

Missing plugins found while loading a transformation on Kettle

Embed Pentaho Data Integration

Extend Pentaho Data Integration

Kettle — 自定義插件重點理解

Kettle — 源碼啟動和代碼結構分析

kettle自定義插件開發-官網demo集成與調式

kettle通用插件[kettlePlugins]使用說明

eclipse遠程調試時出現:Failed to connect to remote VM. Connection refused. Connection refused: connect

Kettle環境初始化源碼分析(KettleEnvironment.init())

Kettle plugin 插件開發

商業智能BI-ETL工具-Kettle的介紹與安裝

kettle7源碼環境搭建及插件源碼調試教程

關於Kettle使用es批量導出插件支持ES5/ES6/ES7的說明

kettle插件加載流程

Pentaho Kettle 8.3 源碼編譯打包及 Debug 調試運行(圖文教程)重點看

https://community.hitachivantara.com/communities/developers

OSGi 系列(一)之什么是 OSGi :Java 語言的動態模塊系統

bat執行exe程序帶參數_Kettle程序入口原理解析

kettle轉換和作業插件開發及調試

kettle 源碼准備與調試

Apache Karaf調研


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM