(BUG)Kettle8.1.0.0-365註冊自定義插件BUG

    寫了兩個kettle插件,這兩個插件是兩個插件項目,且在兩個分類,開發完成打包到放到kettle的plugins目錄下,啓動kettle,結果在kettle設計器中只顯示了一個插件。後來改了插件名稱,插件在plugins文件夾中的文件夾的名字,發現始終是先掃描到誰就能夠註冊進來,後續的無論有幾個都進不來,後果跟了一下kettle的源代碼,發現是kettle裏面存儲插件TreeSet的Comparator寫的有問題。java

    負責註冊插件的類org.pentaho.di.core.plugins.PluginRegistry中聲明瞭一個Map存放全部的插件類型及插件。key是插件類型如做業項插件、步驟插件類型等,value是一個TreeSet,TreeSet中存儲的是改插件類型全部的插件。(Kettle8.1.0.0-365版本在PluginRegistry的91行)this

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。code

    咱們接下來看一下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中,因此第二個及之後的插件都註冊不進來。修改的代碼以下:ci

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;
    };
  }
相關文章
相關標籤/搜索