fix(ft546): isolate shadow from legacy and PLC threads
This commit is contained in:
@@ -9,7 +9,11 @@ public class Ft546WcsProperties {
|
||||
public enum RuntimeMode {
|
||||
LEGACY,
|
||||
SHADOW,
|
||||
STANDARD
|
||||
STANDARD;
|
||||
|
||||
public boolean isLegacy() { return this == LEGACY; }
|
||||
public boolean isShadow() { return this == SHADOW; }
|
||||
public boolean isStandard() { return this == STANDARD; }
|
||||
}
|
||||
|
||||
private RuntimeMode runtimeMode = RuntimeMode.LEGACY;
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.ruoyi.project.ft546.runtime;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor;
|
||||
import org.springframework.boot.env.EnvironmentPostProcessor;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public final class Ft546AdminRuntimeModeEnvironmentPostProcessor
|
||||
implements EnvironmentPostProcessor, org.springframework.core.Ordered {
|
||||
private static final String RUNTIME_MODE_PROPERTY =
|
||||
"ft546.wcs.runtime-mode";
|
||||
|
||||
@Override
|
||||
public void postProcessEnvironment(ConfigurableEnvironment environment,
|
||||
SpringApplication application) {
|
||||
String configured = environment.getProperty(RUNTIME_MODE_PROPERTY);
|
||||
String mode = configured == null
|
||||
? "LEGACY"
|
||||
: configured.trim().toUpperCase(Locale.ROOT);
|
||||
if ("SHADOW".equals(mode)) {
|
||||
throw new IllegalStateException(
|
||||
"ADMIN_SHADOW_MODE_FORBIDDEN_USE_RUOYI_WCS_SHADOW");
|
||||
}
|
||||
if (!"LEGACY".equals(mode) && !"STANDARD".equals(mode)) {
|
||||
throw new IllegalStateException(
|
||||
"UNSUPPORTED_FT546_WCS_RUNTIME_MODE:" + mode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return ConfigDataEnvironmentPostProcessor.ORDER + 1;
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,7 @@ import org.springframework.stereotype.Component;
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -76,6 +77,8 @@ public class ScheduleThread {
|
||||
private volatile boolean isRun2 = false;
|
||||
|
||||
private static final long THREAD_STOP_TIMEOUT_MS = 5000L;
|
||||
private static final String SHADOW_THREAD_REJECTION =
|
||||
"SHADOW mode forbids legacy and PLC thread operations";
|
||||
|
||||
/**
|
||||
* 项目初始化启动
|
||||
@@ -83,12 +86,13 @@ public class ScheduleThread {
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
log.info("初始化调度服务");
|
||||
//读取信息初始化
|
||||
plcReadInit();
|
||||
//调度初始化
|
||||
|
||||
|
||||
scheduleInit();
|
||||
RuntimeCapabilities capabilities = capabilities();
|
||||
if (capabilities.isPlcFactReadersAllowed()) {
|
||||
plcReadInit();
|
||||
}
|
||||
if (capabilities.isLegacyBusinessThreadsAllowed()) {
|
||||
scheduleInit();
|
||||
}
|
||||
log.info("调度服务执行成功");
|
||||
// startAllThread();
|
||||
// startAllThread2();
|
||||
@@ -97,7 +101,7 @@ public class ScheduleThread {
|
||||
|
||||
@EventListener(ApplicationReadyEvent.class)
|
||||
public void startStandardFactReadersAfterApplicationReady() {
|
||||
if (isStandardMode()) {
|
||||
if (capabilities().isAutomaticFactReaderStartAllowed()) {
|
||||
startAllThread();
|
||||
}
|
||||
}
|
||||
@@ -106,7 +110,7 @@ public class ScheduleThread {
|
||||
* 调度初始化
|
||||
*/
|
||||
private void scheduleInit() {
|
||||
if (isStandardMode()) {
|
||||
if (!runtimeMode().isLegacy()) {
|
||||
return;
|
||||
}
|
||||
MonitoringThread monitoringThread = new MonitoringThread();
|
||||
@@ -190,6 +194,7 @@ public class ScheduleThread {
|
||||
* 开始线程
|
||||
*/
|
||||
public synchronized void startAllThread() {
|
||||
requirePlcFactReadersAllowed();
|
||||
if (isRun) {
|
||||
return;
|
||||
}
|
||||
@@ -211,10 +216,7 @@ public class ScheduleThread {
|
||||
* 开启线程2
|
||||
*/
|
||||
public void startAllThread2() {
|
||||
if (isStandardMode()) {
|
||||
throw new IllegalStateException(
|
||||
"legacy business scheduler is disabled in STANDARD mode");
|
||||
}
|
||||
requireLegacyBusinessThreadsAllowed();
|
||||
monitoringCustomThread.thread.start();
|
||||
ssjThreadCustomThread.thread.start();
|
||||
tsjThreadCustomThread.thread.start();
|
||||
@@ -228,6 +230,7 @@ public class ScheduleThread {
|
||||
* 停止线程
|
||||
*/
|
||||
public synchronized void stopAllThread() {
|
||||
requirePlcFactReadersAllowed();
|
||||
if (!isRun) {
|
||||
return;
|
||||
}
|
||||
@@ -341,6 +344,9 @@ public class ScheduleThread {
|
||||
* 停止线程2
|
||||
*/
|
||||
public void stopAllThread2() {
|
||||
if (runtimeMode().isShadow()) {
|
||||
throw new IllegalStateException(SHADOW_THREAD_REJECTION);
|
||||
}
|
||||
if (isStandardMode()) {
|
||||
isRun2 = false;
|
||||
return;
|
||||
@@ -359,16 +365,84 @@ public class ScheduleThread {
|
||||
/**
|
||||
* 获取调度状态
|
||||
*/
|
||||
public HashMap<String,Boolean> getScheduleStatus() {
|
||||
HashMap<String,Boolean> map = new HashMap<>();
|
||||
public HashMap<String, Boolean> getScheduleStatus() {
|
||||
HashMap<String, Boolean> map = new HashMap<String, Boolean>();
|
||||
map.put("isRun",isRun);
|
||||
map.put("isRun2",isRun2);
|
||||
return map;
|
||||
}
|
||||
|
||||
public HashMap<String, Object> getScheduleStatusDetails() {
|
||||
RuntimeCapabilities capabilities = capabilities();
|
||||
HashMap<String, Object> map = new LinkedHashMap<String, Object>();
|
||||
map.putAll(getScheduleStatus());
|
||||
map.put("runtimeMode", runtimeMode().name());
|
||||
map.put("plcFactReadersAllowed",
|
||||
capabilities.isPlcFactReadersAllowed());
|
||||
map.put("legacyBusinessThreadsAllowed",
|
||||
capabilities.isLegacyBusinessThreadsAllowed());
|
||||
map.put("automaticFactReaderStartAllowed",
|
||||
capabilities.isAutomaticFactReaderStartAllowed());
|
||||
return map;
|
||||
}
|
||||
|
||||
private boolean isStandardMode() {
|
||||
return ft546WcsProperties != null
|
||||
&& ft546WcsProperties.getRuntimeMode()
|
||||
== Ft546WcsProperties.RuntimeMode.STANDARD;
|
||||
return runtimeMode().isStandard();
|
||||
}
|
||||
|
||||
private Ft546WcsProperties.RuntimeMode runtimeMode() {
|
||||
return ft546WcsProperties == null
|
||||
? Ft546WcsProperties.RuntimeMode.LEGACY
|
||||
: ft546WcsProperties.getRuntimeMode();
|
||||
}
|
||||
|
||||
private RuntimeCapabilities capabilities() {
|
||||
Ft546WcsProperties.RuntimeMode mode = runtimeMode();
|
||||
return new RuntimeCapabilities(!mode.isShadow(), mode.isLegacy(),
|
||||
mode.isStandard());
|
||||
}
|
||||
|
||||
private void requirePlcFactReadersAllowed() {
|
||||
if (!capabilities().isPlcFactReadersAllowed()) {
|
||||
throw new IllegalStateException(SHADOW_THREAD_REJECTION);
|
||||
}
|
||||
}
|
||||
|
||||
private void requireLegacyBusinessThreadsAllowed() {
|
||||
Ft546WcsProperties.RuntimeMode mode = runtimeMode();
|
||||
if (mode.isShadow()) {
|
||||
throw new IllegalStateException(SHADOW_THREAD_REJECTION);
|
||||
}
|
||||
if (!mode.isLegacy()) {
|
||||
throw new IllegalStateException(
|
||||
"legacy business scheduler is disabled in STANDARD mode");
|
||||
}
|
||||
}
|
||||
|
||||
public static final class RuntimeCapabilities {
|
||||
private final boolean plcFactReadersAllowed;
|
||||
private final boolean legacyBusinessThreadsAllowed;
|
||||
private final boolean automaticFactReaderStartAllowed;
|
||||
|
||||
RuntimeCapabilities(boolean plcFactReadersAllowed,
|
||||
boolean legacyBusinessThreadsAllowed,
|
||||
boolean automaticFactReaderStartAllowed) {
|
||||
this.plcFactReadersAllowed = plcFactReadersAllowed;
|
||||
this.legacyBusinessThreadsAllowed = legacyBusinessThreadsAllowed;
|
||||
this.automaticFactReaderStartAllowed =
|
||||
automaticFactReaderStartAllowed;
|
||||
}
|
||||
|
||||
public boolean isPlcFactReadersAllowed() {
|
||||
return plcFactReadersAllowed;
|
||||
}
|
||||
|
||||
public boolean isLegacyBusinessThreadsAllowed() {
|
||||
return legacyBusinessThreadsAllowed;
|
||||
}
|
||||
|
||||
public boolean isAutomaticFactReaderStartAllowed() {
|
||||
return automaticFactReaderStartAllowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,6 @@ public class ThreadOperationController {
|
||||
*/
|
||||
@RequestMapping("/getScheduleStatus")
|
||||
public AjaxResult getScheduleStatus() {
|
||||
return AjaxResult.success(scheduleThread.getScheduleStatus());
|
||||
return AjaxResult.success(scheduleThread.getScheduleStatusDetails());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,8 +95,8 @@ public class BaseSRgvThread implements Runnable, SRgvThreadInterface {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (isStandardRuntime()) {
|
||||
log.warn("STANDARD mode owns scheduling; legacy SRGV thread will not run");
|
||||
if (!isLegacyRuntime()) {
|
||||
log.warn("Non-LEGACY mode owns scheduling; legacy SRGV thread will not run");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -141,11 +141,10 @@ public class BaseSRgvThread implements Runnable, SRgvThreadInterface {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isStandardRuntime() {
|
||||
private boolean isLegacyRuntime() {
|
||||
Ft546WcsProperties properties =
|
||||
SpringUtils.getBean(Ft546WcsProperties.class);
|
||||
return properties.getRuntimeMode()
|
||||
== Ft546WcsProperties.RuntimeMode.STANDARD;
|
||||
return properties.getRuntimeMode().isLegacy();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
2
ruoyi-admin/src/main/resources/META-INF/spring.factories
Normal file
2
ruoyi-admin/src/main/resources/META-INF/spring.factories
Normal file
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.env.EnvironmentPostProcessor=\
|
||||
com.ruoyi.project.ft546.runtime.Ft546AdminRuntimeModeEnvironmentPostProcessor
|
||||
@@ -0,0 +1,279 @@
|
||||
package com.ruoyi.project.ft546.runtime;
|
||||
|
||||
import com.ruoyi.framework.web.exception.GlobalExceptionHandler;
|
||||
import com.ruoyi.project.ft546.config.Ft546WcsProperties;
|
||||
import com.ruoyi.schedule.ScheduleThread;
|
||||
import com.ruoyi.schedule.ThreadOperationController;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
class Ft546ShadowIsolationTest {
|
||||
private static final String SHADOW_REJECTION =
|
||||
"SHADOW mode forbids legacy and PLC thread operations";
|
||||
private static final AtomicBoolean PROBE_CONSTRUCTED =
|
||||
new AtomicBoolean();
|
||||
|
||||
@Test
|
||||
void shadowInitializationDoesNotConstructPlcOrLegacyThreads() {
|
||||
ScheduleThread runtime = runtime(Ft546WcsProperties.RuntimeMode.SHADOW);
|
||||
|
||||
runtime.init();
|
||||
|
||||
assertTrue(runtime.plcSrgvReadCustomThreads.isEmpty());
|
||||
assertNull(runtime.plcSsjReadCustomThread);
|
||||
assertNull(runtime.plcTsjReadCustomThread);
|
||||
assertNull(runtime.plcCdzReadCustomThread);
|
||||
assertNull(runtime.monitoringCustomThread);
|
||||
assertNull(runtime.ssjThreadCustomThread);
|
||||
assertNull(runtime.sRgvThreadCustomThread);
|
||||
assertNull(runtime.tsjThreadCustomThread);
|
||||
assertNull(runtime.cdzThreadCustomThread);
|
||||
}
|
||||
|
||||
@Test
|
||||
void everyManualThreadOperationFailsClosedInShadow() {
|
||||
ScheduleThread runtime = runtime(Ft546WcsProperties.RuntimeMode.SHADOW);
|
||||
|
||||
assertRejected(runtime::startAllThread);
|
||||
assertRejected(runtime::startAllThread2);
|
||||
assertRejected(runtime::stopAllThread);
|
||||
assertRejected(runtime::stopAllThread2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shadowStatusExplainsThatNoThreadCapabilityIsAllowed() {
|
||||
ScheduleThread runtime = runtime(Ft546WcsProperties.RuntimeMode.SHADOW);
|
||||
|
||||
Map<?, ?> status = runtime.getScheduleStatusDetails();
|
||||
|
||||
assertEquals("SHADOW", status.get("runtimeMode"));
|
||||
assertEquals(Boolean.FALSE, status.get("plcFactReadersAllowed"));
|
||||
assertEquals(Boolean.FALSE, status.get("legacyBusinessThreadsAllowed"));
|
||||
assertEquals(Boolean.FALSE, status.get("automaticFactReaderStartAllowed"));
|
||||
assertEquals(Boolean.FALSE, status.get("isRun"));
|
||||
assertEquals(Boolean.FALSE, status.get("isRun2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void runtimeModePredicatesAreExclusive() {
|
||||
assertTrue(Ft546WcsProperties.RuntimeMode.LEGACY.isLegacy());
|
||||
assertFalse(Ft546WcsProperties.RuntimeMode.LEGACY.isShadow());
|
||||
assertFalse(Ft546WcsProperties.RuntimeMode.LEGACY.isStandard());
|
||||
assertTrue(Ft546WcsProperties.RuntimeMode.SHADOW.isShadow());
|
||||
assertTrue(Ft546WcsProperties.RuntimeMode.STANDARD.isStandard());
|
||||
}
|
||||
|
||||
@Test
|
||||
void adminEnvironmentProcessorRejectsShadowBeforeContextCreation() {
|
||||
StandardEnvironment environment = environment("SHADOW");
|
||||
Ft546AdminRuntimeModeEnvironmentPostProcessor processor =
|
||||
new Ft546AdminRuntimeModeEnvironmentPostProcessor();
|
||||
|
||||
IllegalStateException failure = assertThrows(IllegalStateException.class,
|
||||
() -> processor.postProcessEnvironment(environment,
|
||||
new SpringApplication(Object.class)));
|
||||
|
||||
assertEquals(
|
||||
"ADMIN_SHADOW_MODE_FORBIDDEN_USE_RUOYI_WCS_SHADOW",
|
||||
failure.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void adminEnvironmentProcessorAcceptsMissingLegacyAndStandardModes() {
|
||||
Ft546AdminRuntimeModeEnvironmentPostProcessor processor =
|
||||
new Ft546AdminRuntimeModeEnvironmentPostProcessor();
|
||||
|
||||
processor.postProcessEnvironment(new StandardEnvironment(),
|
||||
new SpringApplication(Object.class));
|
||||
processor.postProcessEnvironment(environment("LEGACY"),
|
||||
new SpringApplication(Object.class));
|
||||
processor.postProcessEnvironment(environment("STANDARD"),
|
||||
new SpringApplication(Object.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void adminEnvironmentProcessorRejectsUnknownModes() {
|
||||
Ft546AdminRuntimeModeEnvironmentPostProcessor processor =
|
||||
new Ft546AdminRuntimeModeEnvironmentPostProcessor();
|
||||
|
||||
IllegalStateException failure = assertThrows(
|
||||
IllegalStateException.class,
|
||||
() -> processor.postProcessEnvironment(environment("unsafe"),
|
||||
new SpringApplication(Object.class)));
|
||||
|
||||
assertEquals("UNSUPPORTED_FT546_WCS_RUNTIME_MODE:UNSAFE",
|
||||
failure.getMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void registeredProcessorRejectsShadowBeforeAnyBeanConstruction() {
|
||||
PROBE_CONSTRUCTED.set(false);
|
||||
SpringApplication application = new SpringApplication(
|
||||
ProbeConfiguration.class);
|
||||
application.setWebApplicationType(WebApplicationType.NONE);
|
||||
|
||||
IllegalStateException failure = assertThrows(IllegalStateException.class,
|
||||
() -> application.run(
|
||||
"--ft546.wcs.runtime-mode=SHADOW",
|
||||
"--spring.main.banner-mode=off",
|
||||
"--logging.level.root=OFF"));
|
||||
|
||||
assertEquals(
|
||||
"ADMIN_SHADOW_MODE_FORBIDDEN_USE_RUOYI_WCS_SHADOW",
|
||||
rootCause(failure).getMessage());
|
||||
assertFalse(PROBE_CONSTRUCTED.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void registeredProcessorReadsConfigDataBeforeBeanConstruction(
|
||||
@TempDir Path tempDir) throws Exception {
|
||||
Path config = tempDir.resolve("application.properties");
|
||||
Files.write(config, Collections.singletonList(
|
||||
"ft546.wcs.runtime-mode=SHADOW"),
|
||||
StandardCharsets.UTF_8);
|
||||
PROBE_CONSTRUCTED.set(false);
|
||||
SpringApplication application = new SpringApplication(
|
||||
ProbeConfiguration.class);
|
||||
application.setWebApplicationType(WebApplicationType.NONE);
|
||||
|
||||
IllegalStateException failure = assertThrows(
|
||||
IllegalStateException.class,
|
||||
() -> application.run(
|
||||
"--spring.config.location="
|
||||
+ config.toUri().toString(),
|
||||
"--spring.main.banner-mode=off",
|
||||
"--logging.level.root=OFF"));
|
||||
|
||||
assertEquals(
|
||||
"ADMIN_SHADOW_MODE_FORBIDDEN_USE_RUOYI_WCS_SHADOW",
|
||||
rootCause(failure).getMessage());
|
||||
assertFalse(PROBE_CONSTRUCTED.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void threadMutationEndpointsKeepTheStableAuthorizationSurface()
|
||||
throws Exception {
|
||||
assertNoNewPermission("startAllThread");
|
||||
assertNoNewPermission("startAllThread2");
|
||||
assertNoNewPermission("stopAllThread");
|
||||
assertNoNewPermission("stopAllThread2");
|
||||
}
|
||||
|
||||
@Test
|
||||
void scheduleStatusKeepsItsStableBinaryReturnType() throws Exception {
|
||||
Method method = ScheduleThread.class.getMethod(
|
||||
"getScheduleStatus");
|
||||
|
||||
assertEquals(HashMap.class, method.getReturnType());
|
||||
assertEquals("java.util.HashMap<java.lang.String, java.lang.Boolean>",
|
||||
method.getGenericReturnType().getTypeName());
|
||||
|
||||
Map<?, ?> stableStatus = runtime(
|
||||
Ft546WcsProperties.RuntimeMode.SHADOW).getScheduleStatus();
|
||||
assertEquals(2, stableStatus.size());
|
||||
assertEquals(Boolean.FALSE, stableStatus.get("isRun"));
|
||||
assertEquals(Boolean.FALSE, stableStatus.get("isRun2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void everyWebThreadMutationReturnsAnErrorInShadow() throws Exception {
|
||||
ThreadOperationController controller =
|
||||
new ThreadOperationController();
|
||||
ReflectionTestUtils.setField(controller, "scheduleThread",
|
||||
runtime(Ft546WcsProperties.RuntimeMode.SHADOW));
|
||||
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(controller)
|
||||
.setControllerAdvice(new GlobalExceptionHandler())
|
||||
.build();
|
||||
|
||||
assertWebRejected(mockMvc, "startAllThread");
|
||||
assertWebRejected(mockMvc, "startAllThread2");
|
||||
assertWebRejected(mockMvc, "stopAllThread");
|
||||
assertWebRejected(mockMvc, "stopAllThread2");
|
||||
}
|
||||
|
||||
private static void assertRejected(Runnable action) {
|
||||
IllegalStateException failure = assertThrows(
|
||||
IllegalStateException.class, action::run);
|
||||
assertEquals(SHADOW_REJECTION, failure.getMessage());
|
||||
}
|
||||
|
||||
private static ScheduleThread runtime(Ft546WcsProperties.RuntimeMode mode) {
|
||||
Ft546WcsProperties properties = new Ft546WcsProperties();
|
||||
properties.setRuntimeMode(mode);
|
||||
ScheduleThread runtime = new ScheduleThread();
|
||||
ReflectionTestUtils.setField(runtime, "ft546WcsProperties", properties);
|
||||
return runtime;
|
||||
}
|
||||
|
||||
private static StandardEnvironment environment(String runtimeMode) {
|
||||
StandardEnvironment environment = new StandardEnvironment();
|
||||
environment.getPropertySources().addFirst(new MapPropertySource(
|
||||
"test", Collections.<String, Object>singletonMap(
|
||||
"ft546.wcs.runtime-mode", runtimeMode)));
|
||||
return environment;
|
||||
}
|
||||
|
||||
private static void assertNoNewPermission(String methodName)
|
||||
throws Exception {
|
||||
Method method = ThreadOperationController.class.getMethod(methodName);
|
||||
PreAuthorize annotation = AnnotationUtils.findAnnotation(
|
||||
method, PreAuthorize.class);
|
||||
assertNull(annotation, methodName
|
||||
+ " must keep the stable authorization surface");
|
||||
}
|
||||
|
||||
private static void assertWebRejected(MockMvc mockMvc, String operation)
|
||||
throws Exception {
|
||||
mockMvc.perform(post("/device/operation/" + operation))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(500))
|
||||
.andExpect(jsonPath("$.msg").value(SHADOW_REJECTION));
|
||||
}
|
||||
|
||||
private static Throwable rootCause(Throwable failure) {
|
||||
Throwable current = failure;
|
||||
while (current.getCause() != null && current.getCause() != current) {
|
||||
current = current.getCause();
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class ProbeConfiguration {
|
||||
@Bean
|
||||
Object constructionProbe() {
|
||||
PROBE_CONSTRUCTED.set(true);
|
||||
return new Object();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user