Skip to content

Commit 6aabf29

Browse files
bullet-toothdmitry-timofeev
authored andcommitted
Add Execution Preconditions [ECR-2746] (#1351)
1 parent 1a246f9 commit 6aabf29

File tree

6 files changed

+352
-27
lines changed

6 files changed

+352
-27
lines changed

exonum-java-binding/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
3333
`ListProof`;
3434
- [`Blockchain`][blockchain-proofs].
3535
- `ProofEntryIndexProxy` collection.
36+
- Transaction precondition utility methods,
37+
see `com.exonum.binding.core.transaction.ExecutionPreconditions`.(#1351)
3638
- `supervisor-mode` CLI parameter added for `generate-template` command. It
3739
allows to configure the mode of the Supervisor service. Possible values are
3840
"simple" and "decentralized". (#1361)

exonum-java-binding/core/src/main/java/com/exonum/binding/core/transaction/ExecutionException.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
* @see Blockchain#getTxResult(HashCode)
5454
* @see Blockchain#getCallErrors(long)
5555
* @see ExecutionStatus
56+
* @see ExecutionPreconditions
5657
*/
5758
public class ExecutionException extends RuntimeException {
5859

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
/*
2+
* Copyright 2020 The Exonum Team
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.exonum.binding.core.transaction;
18+
19+
import static com.google.common.base.Strings.lenientFormat;
20+
21+
import org.checkerframework.checker.nullness.qual.Nullable;
22+
23+
/**
24+
* Utility methods that helps verifying conditions conducted in expression
25+
* while transaction execution.
26+
* If the condition is not met, the {@code ExecutionPreconditions} method
27+
* throws {@link ExecutionException}.
28+
*
29+
* <p>Consider the following example:
30+
* <pre>{@code
31+
* void checkEnoughMoney(long balance, long amount) {
32+
* if(balance < amount) {
33+
* throw new ExecutionException((byte)3, "Not enough money. Operation amount is " + amount
34+
* + ", but actual balance was " + balance);
35+
* }
36+
* }
37+
* }</pre>
38+
*
39+
* <p>which can be replaced using ExecutionPreconditions:
40+
* <pre>{@code
41+
* checkExecution(amount <= balance, (byte)3,
42+
* "Not enough money. Operation amount is %s, but actual balance was %s",
43+
* amount, balance);
44+
* }</pre>
45+
*
46+
* @see ExecutionException
47+
*/
48+
public final class ExecutionPreconditions {
49+
50+
/**
51+
* Verifies the truth of the given expression.
52+
*
53+
* @param expression a boolean expression
54+
* @param errorCode execution error code
55+
* @throws ExecutionException if {@code expression} is false
56+
*/
57+
public static void checkExecution(boolean expression, byte errorCode) {
58+
if (!expression) {
59+
throw new ExecutionException(errorCode);
60+
}
61+
}
62+
63+
/**
64+
* Verifies the truth of the given expression.
65+
*
66+
* @param expression a boolean expression
67+
* @param errorCode execution error code
68+
* @param errorMessage execution error description to use if the check fails
69+
* @throws ExecutionException if {@code expression} is false
70+
*/
71+
public static void checkExecution(boolean expression, byte errorCode,
72+
@Nullable Object errorMessage) {
73+
if (!expression) {
74+
throw new ExecutionException(errorCode, String.valueOf(errorMessage));
75+
}
76+
}
77+
78+
/**
79+
* Verifies the truth of the given expression.
80+
*
81+
* @param expression a boolean expression
82+
* @param errorCode execution error code
83+
* @param errorMessageTemplate execution error description template to use if the check fails.
84+
* The template could have placeholders {@code %s} which will be replaced by arguments
85+
* resolved by position
86+
* @param errorMessageArgs arguments to be used in the template. Each argument will be converted
87+
* to string using {@link String#valueOf(Object)}
88+
* @throws ExecutionException if {@code expression} is false
89+
*/
90+
public static void checkExecution(boolean expression, byte errorCode,
91+
@Nullable String errorMessageTemplate,
92+
@Nullable Object... errorMessageArgs) {
93+
if (!expression) {
94+
throw new ExecutionException(errorCode,
95+
lenientFormat(errorMessageTemplate, errorMessageArgs));
96+
}
97+
}
98+
99+
/**
100+
* Verifies the truth of the given expression.
101+
*
102+
* <p>See {@link #checkExecution(boolean, byte, String, Object...)} for details.
103+
*/
104+
public static void checkExecution(boolean expression, byte errorCode,
105+
@Nullable String errorMessageTemplate,
106+
int arg1) {
107+
if (!expression) {
108+
throw new ExecutionException(errorCode, lenientFormat(errorMessageTemplate, arg1));
109+
}
110+
}
111+
112+
/**
113+
* Verifies the truth of the given expression.
114+
*
115+
* <p>See {@link #checkExecution(boolean, byte, String, Object...)} for details.
116+
*/
117+
public static void checkExecution(boolean expression, byte errorCode,
118+
@Nullable String errorMessageTemplate,
119+
int arg1, int arg2) {
120+
if (!expression) {
121+
throw new ExecutionException(errorCode, lenientFormat(errorMessageTemplate, arg1, arg2));
122+
}
123+
}
124+
125+
/**
126+
* Verifies the truth of the given expression.
127+
*
128+
* <p>See {@link #checkExecution(boolean, byte, String, Object...)} for details.
129+
*/
130+
public static void checkExecution(boolean expression, byte errorCode,
131+
@Nullable String errorMessageTemplate,
132+
int arg1, long arg2) {
133+
if (!expression) {
134+
throw new ExecutionException(errorCode, lenientFormat(errorMessageTemplate, arg1, arg2));
135+
}
136+
}
137+
138+
/**
139+
* Verifies the truth of the given expression.
140+
*
141+
* <p>See {@link #checkExecution(boolean, byte, String, Object...)} for details.
142+
*/
143+
public static void checkExecution(boolean expression, byte errorCode,
144+
@Nullable String errorMessageTemplate,
145+
int arg1, @Nullable Object arg2) {
146+
if (!expression) {
147+
throw new ExecutionException(errorCode, lenientFormat(errorMessageTemplate, arg1, arg2));
148+
}
149+
}
150+
151+
/**
152+
* Verifies the truth of the given expression.
153+
*
154+
* <p>See {@link #checkExecution(boolean, byte, String, Object...)} for details.
155+
*/
156+
public static void checkExecution(boolean expression, byte errorCode,
157+
@Nullable String errorMessageTemplate,
158+
long arg1) {
159+
if (!expression) {
160+
throw new ExecutionException(errorCode, lenientFormat(errorMessageTemplate, arg1));
161+
}
162+
}
163+
164+
/**
165+
* Verifies the truth of the given expression.
166+
*
167+
* <p>See {@link #checkExecution(boolean, byte, String, Object...)} for details.
168+
*/
169+
public static void checkExecution(boolean expression, byte errorCode,
170+
@Nullable String errorMessageTemplate,
171+
long arg1, int arg2) {
172+
if (!expression) {
173+
throw new ExecutionException(errorCode, lenientFormat(errorMessageTemplate, arg1, arg2));
174+
}
175+
}
176+
177+
/**
178+
* Verifies the truth of the given expression.
179+
*
180+
* <p>See {@link #checkExecution(boolean, byte, String, Object...)} for details.
181+
*/
182+
public static void checkExecution(boolean expression, byte errorCode,
183+
@Nullable String errorMessageTemplate,
184+
long arg1, long arg2) {
185+
if (!expression) {
186+
throw new ExecutionException(errorCode, lenientFormat(errorMessageTemplate, arg1, arg2));
187+
}
188+
}
189+
190+
/**
191+
* Verifies the truth of the given expression.
192+
*
193+
* <p>See {@link #checkExecution(boolean, byte, String, Object...)} for details.
194+
*/
195+
public static void checkExecution(boolean expression, byte errorCode,
196+
@Nullable String errorMessageTemplate,
197+
long arg1, @Nullable Object arg2) {
198+
if (!expression) {
199+
throw new ExecutionException(errorCode, lenientFormat(errorMessageTemplate, arg1, arg2));
200+
}
201+
}
202+
203+
/**
204+
* Verifies the truth of the given expression.
205+
*
206+
* <p>See {@link #checkExecution(boolean, byte, String, Object...)} for details.
207+
*/
208+
public static void checkExecution(boolean expression, byte errorCode,
209+
@Nullable String errorMessageTemplate,
210+
@Nullable Object arg1) {
211+
if (!expression) {
212+
throw new ExecutionException(errorCode, lenientFormat(errorMessageTemplate, arg1));
213+
}
214+
}
215+
216+
/**
217+
* Verifies the truth of the given expression.
218+
*
219+
* <p>See {@link #checkExecution(boolean, byte, String, Object...)} for details.
220+
*/
221+
public static void checkExecution(boolean expression, byte errorCode,
222+
@Nullable String errorMessageTemplate,
223+
@Nullable Object arg1, int arg2) {
224+
if (!expression) {
225+
throw new ExecutionException(errorCode, lenientFormat(errorMessageTemplate, arg1, arg2));
226+
}
227+
}
228+
229+
/**
230+
* Verifies the truth of the given expression.
231+
*
232+
* <p>See {@link #checkExecution(boolean, byte, String, Object...)} for details.
233+
*/
234+
public static void checkExecution(boolean expression, byte errorCode,
235+
@Nullable String errorMessageTemplate,
236+
@Nullable Object arg1, long arg2) {
237+
if (!expression) {
238+
throw new ExecutionException(errorCode, lenientFormat(errorMessageTemplate, arg1, arg2));
239+
}
240+
}
241+
242+
/**
243+
* Verifies the truth of the given expression.
244+
*
245+
* <p>See {@link #checkExecution(boolean, byte, String, Object...)} for details.
246+
*/
247+
public static void checkExecution(boolean expression, byte errorCode,
248+
@Nullable String errorMessageTemplate,
249+
@Nullable Object arg1, @Nullable Object arg2) {
250+
if (!expression) {
251+
throw new ExecutionException(errorCode, lenientFormat(errorMessageTemplate, arg1, arg2));
252+
}
253+
}
254+
255+
/**
256+
* Verifies the truth of the given expression.
257+
*
258+
* <p>See {@link #checkExecution(boolean, byte, String, Object...)} for details.
259+
*/
260+
public static void checkExecution(boolean expression, byte errorCode,
261+
@Nullable String errorMessageTemplate,
262+
@Nullable Object arg1, @Nullable Object arg2, @Nullable Object arg3) {
263+
if (!expression) {
264+
throw new ExecutionException(errorCode,
265+
lenientFormat(errorMessageTemplate, arg1, arg2, arg3));
266+
}
267+
}
268+
269+
private ExecutionPreconditions() {
270+
}
271+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright 2020 The Exonum Team
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.exonum.binding.core.transaction;
18+
19+
import static org.assertj.core.api.Assertions.assertThat;
20+
import static org.junit.jupiter.api.Assertions.assertThrows;
21+
22+
import org.junit.jupiter.api.Test;
23+
24+
class ExecutionPreconditionsTest {
25+
private static final byte TEST_ERROR_CODE = 1;
26+
27+
@Test
28+
void trueConditionDoesNothing() {
29+
ExecutionPreconditions.checkExecution(true, TEST_ERROR_CODE);
30+
}
31+
32+
@Test
33+
void errorCodeIsPresent() {
34+
ExecutionException e = assertThrows(ExecutionException.class,
35+
() -> ExecutionPreconditions.checkExecution(false, TEST_ERROR_CODE));
36+
37+
assertThat(e.getErrorCode()).isEqualTo(TEST_ERROR_CODE);
38+
}
39+
40+
@Test
41+
void errorDescriptionIsPresent() {
42+
String description = "evil error";
43+
ExecutionException e = assertThrows(ExecutionException.class,
44+
() -> ExecutionPreconditions.checkExecution(false, TEST_ERROR_CODE, description));
45+
46+
assertThat(e.getErrorCode()).isEqualTo(TEST_ERROR_CODE);
47+
assertThat(e).hasMessage(description);
48+
}
49+
50+
@Test
51+
void nullableDescription() {
52+
ExecutionException e = assertThrows(ExecutionException.class,
53+
() -> ExecutionPreconditions.checkExecution(false, TEST_ERROR_CODE, null));
54+
55+
assertThat(e).hasMessage("null");
56+
}
57+
58+
@Test
59+
void errorDescriptionFormat() {
60+
int p1 = 10;
61+
int p2 = 20;
62+
63+
ExecutionException e = assertThrows(ExecutionException.class,
64+
() -> ExecutionPreconditions.checkExecution(p1 == p2, TEST_ERROR_CODE, "%s != %s", p1, p2));
65+
66+
assertThat(e).hasMessage("10 != 20");
67+
}
68+
}

0 commit comments

Comments
 (0)