我创建了一个实现状态机的Java项目。其背后的想法是,SM本身在后台线程中运行,就像在代表不同动作的对象数组上的简单循环一样。
有问题的动作来自另一个名为ActionPack的对象,其中包含Action对象。
使用Java反射在运行时查看实例化的ActionPack并将其导入到状态机中,然后将它们推入数组中,效果很好。然后,状态机在被告知运行时遍历数组,并调用每个动作的execute方法。
正如我所说,这很好,并且所有单元测试都是绿色的……但是只有当我从IntelliJ中运行它们时才可以。如果我尝试通过“ mvn测试”运行它们,则反射代码无法在操作包中找到这些类。
这是失败的反射方法:
/**
* 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 test运行时:
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调用,则无法在其中找到类。
当我调用importActionPack时,失败发生在单元测试的setup()的最后一行中
@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)
Action Pack在无限状态机通用中具有一个接口:
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();
}
}
}
}
摘要
我不知道这是我使用Reflections库的方式是否有问题,因为当我从IntelliJ运行测试时,我希望它会失败...
那么也许这是Maven的问题?
我已经构建并本地安装了所有项目,因此,目前肯定可以使用并构建它们。
仅供参考-目前,我已将单元测试放在各自的项目中,以防止在将项目推送到Nexus时此问题破坏构建。
对于调试此问题的任何帮助或指导,将不胜感激。
布拉德