Skip to content

Commit d8f43fb

Browse files
committed
refactor(TransactionRegister): enhance actuator registration
Changes: - Add registration status check to prevent duplicate initialization - Implement proper error handling with TronError exception throwing - Optimize package scanning scope from "org.tron" to "org.tron.core.actuator"
1 parent 7015fa3 commit d8f43fb

File tree

3 files changed

+197
-10
lines changed

3 files changed

+197
-10
lines changed
Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,49 @@
11
package org.tron.core.utils;
22

33
import java.util.Set;
4+
import java.util.concurrent.atomic.AtomicBoolean;
45
import lombok.extern.slf4j.Slf4j;
56
import org.reflections.Reflections;
67
import org.tron.core.actuator.AbstractActuator;
8+
import org.tron.core.exception.TronError;
79

810
@Slf4j(topic = "TransactionRegister")
911
public class TransactionRegister {
1012

13+
private static final AtomicBoolean REGISTERED = new AtomicBoolean(false);
14+
private static final String PACKAGE_NAME = "org.tron.core.actuator";
15+
1116
public static void registerActuator() {
12-
Reflections reflections = new Reflections("org.tron");
13-
Set<Class<? extends AbstractActuator>> subTypes = reflections
14-
.getSubTypesOf(AbstractActuator.class);
15-
for (Class _class : subTypes) {
16-
try {
17-
_class.newInstance();
18-
} catch (Exception e) {
19-
logger.error("{} contract actuator register fail!", _class, e);
17+
if (REGISTERED.get()) {
18+
logger.info("Actuator already registered.");
19+
return;
20+
}
21+
22+
synchronized (TransactionRegister.class) {
23+
if (REGISTERED.get()) {
24+
logger.info("Actuator already registered.");
25+
return;
26+
}
27+
28+
logger.info("Register actuator start.");
29+
Reflections reflections = new Reflections(PACKAGE_NAME);
30+
Set<Class<? extends AbstractActuator>> subTypes = reflections
31+
.getSubTypesOf(AbstractActuator.class);
32+
33+
for (Class<? extends AbstractActuator> clazz : subTypes) {
34+
try {
35+
logger.debug("Registering actuator: {} start", clazz.getName());
36+
clazz.getDeclaredConstructor().newInstance();
37+
logger.debug("Registering actuator: {} done", clazz.getName());
38+
} catch (Exception e) {
39+
throw new TronError(clazz.getName() + ": "
40+
+ (e.getCause() == null ? e.getMessage() : e.getCause().getMessage()),
41+
e, TronError.ErrCode.ACTUATOR_REGISTER);
42+
}
2043
}
44+
45+
REGISTERED.set(true);
46+
logger.info("Register actuator done, total {}.", subTypes.size());
2147
}
2248
}
23-
2449
}

common/src/main/java/org/tron/core/exception/TronError.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ public enum ErrCode {
4747
LOG_LOAD(1),
4848
WITNESS_INIT(1),
4949
RATE_LIMITER_INIT(1),
50-
SOLID_NODE_INIT(0);
50+
SOLID_NODE_INIT(0),
51+
ACTUATOR_REGISTER(2);
5152

5253
private final int code;
5354

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package org.tron.core.utils;
2+
3+
import static org.junit.Assert.assertEquals;
4+
import static org.junit.Assert.assertFalse;
5+
import static org.junit.Assert.assertThrows;
6+
import static org.junit.Assert.assertTrue;
7+
import static org.mockito.Mockito.mockConstruction;
8+
import static org.mockito.Mockito.when;
9+
10+
import java.lang.reflect.Field;
11+
import java.util.Collections;
12+
import java.util.HashSet;
13+
import java.util.concurrent.atomic.AtomicBoolean;
14+
import org.junit.After;
15+
import org.junit.Before;
16+
import org.junit.Test;
17+
import org.junit.runner.RunWith;
18+
import org.mockito.MockedConstruction;
19+
import org.mockito.junit.MockitoJUnitRunner;
20+
import org.reflections.Reflections;
21+
import org.tron.core.actuator.AbstractActuator;
22+
import org.tron.core.actuator.TransferActuator;
23+
import org.tron.core.config.args.Args;
24+
import org.tron.core.exception.TronError;
25+
26+
@RunWith(MockitoJUnitRunner.class)
27+
public class TransactionRegisterTest {
28+
29+
@Before
30+
public void init() throws Exception {
31+
Args.getInstance().setActuatorSet(new HashSet<>());
32+
resetRegisteredField();
33+
}
34+
35+
@After
36+
public void destroy() {
37+
Args.clearParam();
38+
}
39+
40+
private void resetRegisteredField() throws Exception {
41+
Field registeredField = TransactionRegister.class.getDeclaredField("REGISTERED");
42+
registeredField.setAccessible(true);
43+
AtomicBoolean registered = (AtomicBoolean) registeredField.get(null);
44+
registered.set(false);
45+
}
46+
47+
@Test
48+
public void testAlreadyRegisteredSkipRegistration() throws Exception {
49+
50+
TransactionRegister.registerActuator();
51+
52+
Field registeredField = TransactionRegister.class.getDeclaredField("REGISTERED");
53+
registeredField.setAccessible(true);
54+
AtomicBoolean registered = (AtomicBoolean) registeredField.get(null);
55+
assertTrue("First registration should be completed", registered.get());
56+
57+
TransactionRegister.registerActuator();
58+
assertTrue("Registration should still be true", registered.get());
59+
}
60+
61+
@Test
62+
public void testConcurrentAccessThreadSafe() throws Exception {
63+
final int threadCount = 5;
64+
Thread[] threads = new Thread[threadCount];
65+
final AtomicBoolean testPassed = new AtomicBoolean(true);
66+
67+
for (int i = 0; i < threadCount; i++) {
68+
threads[i] = new Thread(() -> {
69+
try {
70+
TransactionRegister.registerActuator();
71+
} catch (Exception e) {
72+
testPassed.set(false);
73+
}
74+
});
75+
}
76+
77+
for (Thread thread : threads) {
78+
thread.start();
79+
}
80+
81+
for (Thread thread : threads) {
82+
thread.join();
83+
}
84+
85+
assertTrue("All threads should complete successfully", testPassed.get());
86+
87+
Field registeredField = TransactionRegister.class.getDeclaredField("REGISTERED");
88+
registeredField.setAccessible(true);
89+
AtomicBoolean registered = (AtomicBoolean) registeredField.get(null);
90+
assertTrue("Registration should be completed", registered.get());
91+
}
92+
93+
@Test
94+
public void testDoubleCheckLockingAtomicBoolean() throws Exception {
95+
Field registeredField = TransactionRegister.class.getDeclaredField("REGISTERED");
96+
registeredField.setAccessible(true);
97+
AtomicBoolean registered = (AtomicBoolean) registeredField.get(null);
98+
99+
assertFalse("Initial registration state should be false", registered.get());
100+
101+
TransactionRegister.registerActuator();
102+
assertTrue("After first call, should be registered", registered.get());
103+
104+
TransactionRegister.registerActuator();
105+
assertTrue("After second call, should still be registered", registered.get());
106+
}
107+
108+
@Test
109+
public void testSynchronizationBlock() throws Exception {
110+
final AtomicBoolean completedRegistration = new AtomicBoolean(false);
111+
112+
Thread registrationThread = new Thread(() -> {
113+
TransactionRegister.registerActuator();
114+
completedRegistration.set(true);
115+
116+
});
117+
118+
registrationThread.start();
119+
registrationThread.join();
120+
121+
assertTrue("Registration should have completed", completedRegistration.get());
122+
123+
Field registeredField = TransactionRegister.class.getDeclaredField("REGISTERED");
124+
registeredField.setAccessible(true);
125+
AtomicBoolean registered = (AtomicBoolean) registeredField.get(null);
126+
assertTrue("Should be registered after completion", registered.get());
127+
}
128+
129+
@Test
130+
public void testMultipleCallsConsistency() throws Exception {
131+
Field registeredField = TransactionRegister.class.getDeclaredField("REGISTERED");
132+
registeredField.setAccessible(true);
133+
AtomicBoolean registered = (AtomicBoolean) registeredField.get(null);
134+
135+
assertFalse("Should start unregistered", registered.get());
136+
137+
TransactionRegister.registerActuator();
138+
139+
assertTrue("Should be registered after first call", registered.get());
140+
141+
for (int i = 0; i < 5; i++) {
142+
TransactionRegister.registerActuator();
143+
assertTrue("Should remain registered after call " + (i + 2), registered.get());
144+
}
145+
}
146+
147+
@Test
148+
public void testThrowsTronError() {
149+
try (MockedConstruction<Reflections> ignored = mockConstruction(Reflections.class,
150+
(mock, context) -> when(mock.getSubTypesOf(AbstractActuator.class))
151+
.thenReturn(Collections.singleton(TransferActuator.class)));
152+
MockedConstruction<TransferActuator> ignored1 = mockConstruction(TransferActuator.class,
153+
(mock, context) -> {
154+
throw new RuntimeException("boom");
155+
})) {
156+
TronError error = assertThrows(TronError.class, TransactionRegister::registerActuator);
157+
assertEquals(TronError.ErrCode.ACTUATOR_REGISTER, error.getErrCode());
158+
assertTrue(error.getMessage().contains("TransferActuator"));
159+
}
160+
}
161+
}

0 commit comments

Comments
 (0)