Странная проблема с использованием отражения Java в нескольких проектах - PullRequest
0 голосов
/ 11 ноября 2018

Я создал проект Java, который реализует конечный автомат. Идея заключается в том, что сам SM работает в фоновом потоке в виде простого цикла над массивом объектов, представляющих различные действия.

Указанные действия относятся к другому объекту, который называется ActionPack и содержит объекты Action.

Это хорошо работает с использованием отражения Java для просмотра экземпляра ActionPack во время выполнения и импорта их в конечный автомат, где они помещаются в массив. Затем конечный автомат выполняет итерацию по массиву, когда ему приказывают запустить, и вызывает метод execute каждого действия.

Как я уже сказал, это работает хорошо, и все модульные тесты зеленые ... но только когда я запускаю их из IntelliJ. Если я попытаюсь запустить их через «mvn test», код Reflection не сможет найти классы в пакете действий.

Это неверный метод отражения:

  /**
   * Use introspection to read in all of the classes in a given action pack
   * Filter out the ones that are actions and return them in an array
   */
  public ArrayList getActionsFromActionPack(
    DataAccessLayer dataAccessLayer,
    String runRoot
  ) {

    String packageName = this.getClass().getPackage().getName();

    System.out.println("PACKAGE NAME = " + packageName);

    List<ClassLoader> classLoadersList = new LinkedList<>();
    classLoadersList.add(ClasspathHelper.contextClassLoader());
    classLoadersList.add(ClasspathHelper.staticClassLoader());

    Reflections reflections = new Reflections(new ConfigurationBuilder()
      .setScanners(new SubTypesScanner(false /* don't exclude Object.class */), new ResourcesScanner())
      .setUrls(ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[0])))
      .filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix(packageName))));

    Set<Class<? extends Action>> classes = reflections.getSubTypesOf(Action.class);

    System.out.println("Number of classes = " + classes.size());

    ArrayList<IAction> actions = new ArrayList<>();

    for (Class a : classes) {
      try {
        Action action = (Action) Class.forName(a.getName()).newInstance();
        // Give the action access to the DAL and path to control directory
        action.setDataAccessLayer(dataAccessLayer);
        action.setRunRoot(runRoot);
        actions.add(action);
      } catch (ClassNotFoundException e) {
        e.printStackTrace();
        System.err.println(e.getClass().getName() + ": " + e.getMessage());
        System.exit(1);
      } catch (IllegalAccessException e) {
        e.printStackTrace();
        System.err.println(e.getClass().getName() + ": " + e.getMessage());
        System.exit(1);
      } catch (InstantiationException e) {
        e.printStackTrace();
        System.err.println(e.getClass().getName() + ": " + e.getMessage());
        System.exit(1);
      }
    }
    return actions;
  }

С этими отладочными операторами печати, когда я запускаю модульные тесты из IntelliJ, я вижу такой вывод:

    PACKAGE NAME = com.github.museadmin.infinite_state_machine.core.action_pack
    Number of classes = 7
    PACKAGE NAME = com.github.museadmin.infinite_state_machine.core.action_pack
    Number of classes = 7
    PACKAGE NAME = com.github.museadmin.infinite_state_machine.core.action_pack
    Number of classes = 7
    PACKAGE NAME = com.github.museadmin.infinite_state_machine.test.classes
    Number of classes = 1
    PACKAGE NAME = com.github.museadmin.infinite_state_machine.core.action_pack
    Number of classes = 7
    PACKAGE NAME = com.github.museadmin.infinite_state_machine.core.action_pack
    Number of classes = 7

    Process finished with exit code 0

Но когда я запускаю тест mvn:

    PACKAGE NAME = com.github.museadmin.infinite_state_machine.core.action_pack
    Number of classes = 0
    Exception in thread "UnitTestThread" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
        at java.util.ArrayList.rangeCheck(ArrayList.java:653)
        at java.util.ArrayList.get(ArrayList.java:429)
        at com.github.museadmin.infinite_state_machine.core.InfiniteStateMachine.run(InfiniteStateMachine.java:36)
        at java.lang.Thread.run(Thread.java:745)

Похоже, что рефлексивный метод правильно идентифицирует пакет, в котором мы находимся, но затем не может найти внутри него классы, если он вызывается через maven.

Ошибка возникает в последней строке setup () модульного теста, когда я вызываю importActionPack

      @Before
      public void setup() {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        URL is = loader.getResource(PROPERTIES);
        ismCoreActionPack = new ISMCoreActionPack();
        infiniteStateMachine = new InfiniteStateMachine(is.getPath());
        infiniteStateMachine.importActionPack(ismCoreActionPack);
      }

Этот код распространяется на три проекта:

infinite-state-machine (The state machine itself)
infinite-state-machine-common (Where the action pack and action types are defined)
infinite-state-machine-core (The action pack I'm failing to import)

Пакет действий имеет интерфейс с общим автоматом:

    package com.github.museadmin.infinite_state_machine.common.action;

    import com.github.museadmin.infinite_state_machine.common.dal.DataAccessLayer;
    import org.json.JSONObject;
    import java.util.ArrayList;

    public interface IActionPack {
      JSONObject getJsonObjectFromResourceFile(String fileName);
      ArrayList getActionsFromActionPack(
        DataAccessLayer dataAccessLayer,
        String runRoot
      );
    }

Фактическая реализация пакета действий также находится в общем состоянии с бесконечным числом состояний:

    public class ActionPack implements IActionPack

И именно здесь находится метод отражений public ArrayList getActionsFromActionPack ().

Класс Action и его интерфейс также находятся в общем состоянии с бесконечным числом состояний:

    package com.github.museadmin.infinite_state_machine.common.action;

    import com.github.museadmin.infinite_state_machine.common.dal.DataAccessLayer;
    import org.json.JSONObject;

    import java.util.ArrayList;

    public interface IAction {
      void execute();

      Boolean active();
      boolean active(String action);
      void activate(String actionName);
      Boolean afterActionsComplete();
      Boolean beforeActionsComplete();
      void clearPayload(String actionName);
      String createRunDirectory(String directory);
      void deactivate();
      void deactivate(String actionFlag);
      JSONObject getJsonObjectFromFile(String fileName);
      ArrayList<JSONObject> getUnprocessedMessages();
      void insertMessage(JSONObject message);
      void insertProperty(String property, String value);
      void markMessageProcessed(Integer id);
      void setDataAccessLayer(DataAccessLayer dataAccessLayer);
      void setRunRoot(String runRoot);
      String queryProperty(String property);
      String queryRunPhase();
      void setState(String stateName);
      void updatePayload(String actionName, String payload);
      void updateProperty(String property, String value);
      void updateRunPhase(String runPhase);
      void unsetState(String stateName);
    }

Сам родительский класс Action слишком велик для размещения здесь, но он определен в том же пакете:

public abstract class Action implements IAction {

}

Если это поможет, это типичное действие основного пакета, показывающее метод execute:

    package com.github.museadmin.infinite_state_machine.core.action_pack;

    import com.github.museadmin.infinite_state_machine.common.action.Action;

    /**
     * Action that verifies the state of the ISM and confirms
     * we have booted up correctly and can now run
     */
    public class ActionConfirmReadyToRun extends Action {

      /**
       * Confirm we've got through the bootstrapping process ok.
       * and all BEFORE actions completed
       */
      public void execute() {
        if (active()) {
          if (beforeActionsComplete()) {
            setState("READY_TO_RUN");
            updateRunPhase("RUNNING");
            deactivate();
          }
        }
      }
    }
* * 1 042 * 1043 РЕЗЮМЕ *

Я не знаю, если это связано с тем, как я использую библиотеку Reflections, так как я ожидаю, что она потерпит неудачу при запуске тестов из IntelliJ ...

Так, возможно, это проблема с Maven?

Я собрал и локально установил все проекты, поэтому они точно доступны и созданы на этом этапе.

К вашему сведению - на данный момент я поместил модульные тесты в отдельный проект, чтобы эта проблема не сломалась, когда я переносил проекты на Nexus.

Буду очень признателен за любую помощь или руководство по устранению этой проблемы.

Бред

...