Я создал проект 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.
Буду очень признателен за любую помощь или руководство по устранению этой проблемы.
Бред