(BUG)Kettle8.1.0.0-365註冊自定義外掛BUG
阿新 • • 發佈:2019-03-03
寫了兩個kettle外掛,這兩個外掛是兩個外掛專案,且在兩個分類,開發完成打包到放到kettle的plugins目錄下,啟動kettle,結果在kettle設計器中只顯示了一個外掛。後來改了外掛名稱,外掛在plugins資料夾中的資料夾的名字,發現始終是先掃描到誰就可以註冊進來,後續的不管有幾個都進不來,後果跟了一下kettle的原始碼,發現是kettle裡面儲存外掛TreeSet的Comparator寫的有問題。
負責註冊外掛的類org.pentaho.di.core.plugins.PluginRegistry中聲明瞭一個Map存放所有的外掛型別及外掛。key是外掛型別如作業項外掛、步驟外掛型別等,value是一個TreeSet,TreeSet中儲存的是改外掛型別所有的外掛。(Kettle8.1.0.0-365版本在PluginRegistry的91行)
private final Map<Class<? extends PluginTypeInterface>, Set<String>> categoryMap = new HashMap<>();
下面看一下注冊外掛的方法
public void registerPlugin( Class<? extends PluginTypeInterface> pluginType, PluginInterface plugin ) throws KettlePluginException { boolean changed = false; // Is this an add or an update? lock.writeLock().lock(); try { if ( plugin.getIds()[0] == null ) { throw new KettlePluginException( "Not a valid id specified in plugin :" + plugin ); } // Keep the list of plugins sorted by name... // Set<PluginInterface> list = pluginMap.computeIfAbsent( pluginType, k -> new TreeSet<>( Plugin.nullStringComparator ) ); if ( !list.add( plugin ) ) { list.remove( plugin ); list.add( plugin ); changed = true; } if ( !Utils.isEmpty( plugin.getCategory() ) ) { // Keep categories sorted in the natural order here too! // categoryMap.computeIfAbsent( pluginType, k -> new TreeSet<>(getNaturalCategoriesOrderComparator(pluginType ))) .add( plugin.getCategory() ); } } finally { lock.writeLock().unlock(); Set<PluginTypeListener> listeners = this.listeners.get( pluginType ); if ( listeners != null ) { for ( PluginTypeListener listener : listeners ) { // Changed or added? if ( changed ) { listener.pluginChanged( plugin ); } else { listener.pluginAdded( plugin ); } } } synchronized ( this ) { notifyAll(); } } }
重點看方法中的這一行程式碼:
categoryMap.computeIfAbsent(
pluginType, k -> new TreeSet<>(getNaturalCategoriesOrderComparator(pluginType )))
.add( plugin.getCategory() );
這一行是當map中不存在指定的ke時,返回一個空的TreeSet.關鍵是TreeSet指定了一個Comparator。
我們接下來看一下getNaturalCategoriesOrderComparator這個方法是怎麼建立Comparator的。這個方法的目的是對Kettle自身的外掛進行排序按順序顯示。
private static Comparator<String> getNaturalCategoriesOrderComparator(
Class<? extends PluginTypeInterface> pluginType ) {
PluginTypeCategoriesOrder naturalOrderAnnotation =
pluginType.getAnnotation( PluginTypeCategoriesOrder.class );
final String[] naturalOrder;
if ( naturalOrderAnnotation != null ) {
String[] naturalOrderKeys = naturalOrderAnnotation.getNaturalCategoriesOrder();
Class<?> i18nClass = naturalOrderAnnotation.i18nPackageClass();
naturalOrder = Arrays.stream( naturalOrderKeys )
.map( key -> BaseMessages.getString( i18nClass, key ) )
.toArray( String[]::new );
} else {
naturalOrder = null;
}
return ( s1, s2 ) -> {
if ( naturalOrder != null ) {
int idx1 = Const.indexOfString( s1, naturalOrder );
int idx2 = Const.indexOfString( s2, naturalOrder );
return idx1 - idx2;
}
return 0;
};
}
按照這個方式的實現,自定了兩個外掛且外掛在兩個分類中且分類不是Kettle原有的分類,那麼idx1和idx2的值均是-1,如此compare就會返回0,0表示連個分類是相同的就新增不到set中,所以第二個及以後的外掛都註冊不進來。修改的程式碼如下:
private static Comparator<String> getNaturalCategoriesOrderComparator(
Class<? extends PluginTypeInterface> pluginType ) {
PluginTypeCategoriesOrder naturalOrderAnnotation =
pluginType.getAnnotation( PluginTypeCategoriesOrder.class );
........
return ( s1, s2 ) -> {
if ( naturalOrder != null ) {
int idx1 = Const.indexOfString( s1, naturalOrder );
int idx2 = Const.indexOfString( s2, naturalOrder );
// 兩個外掛分類都不是Kettle自己已有的分類是,則讓兩個分類進行進行比較
if (-1 == idx1 && -1 == idx2) return s1.compareTo(s2);
// 自定義的外掛顯示在末尾
if (-1 == idx1 || -1 == idx2) return 1;
return idx1 - idx2;
}
return 0;
};
}