Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions exonum-java-binding/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
`ListProof`;
- [`Blockchain`][blockchain-proofs].
- `ProofEntryIndexProxy` collection.
- Transaction precondition utility methods,
see `com.exonum.binding.core.transaction.ExecutionPreconditions`.(#1351)
- `supervisor-mode` CLI parameter added for `generate-template` command. It
allows to configure the mode of the Supervisor service. Possible values are
"simple" and "decentralized". (#1361)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
* @see Blockchain#getTxResult(HashCode)
* @see Blockchain#getCallErrors(long)
* @see ExecutionStatus
* @see ExecutionPreconditions
*/
public class ExecutionException extends RuntimeException {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
/*
* Copyright 2020 The Exonum Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.exonum.binding.core.transaction;

import static com.google.common.base.Strings.lenientFormat;

import org.checkerframework.checker.nullness.qual.Nullable;

/**
* Utility methods that helps verifying conditions conducted in expression
* while transaction execution.
* If the condition is not met, the {@code ExecutionPreconditions} method
* throws {@link ExecutionException}.
*
* <p>Consider the following example:
* <pre>{@code
* void checkEnoughMoney(long balance, long amount) {
* if(balance < amount) {
* throw new ExecutionException((byte)3, "Not enough money. Operation amount is " + amount
* + ", but actual balance was " + balance);
* }
* }
* }</pre>
*
* <p>which can be replaced using ExecutionPreconditions:
* <pre>{@code
* checkExecution(amount <= balance, (byte)3,
* "Not enough money. Operation amount is %s, but actual balance was %s",
* amount, balance);
* }</pre>
*
* @see ExecutionException
*/
public final class ExecutionPreconditions {

/**
* Verifies the truth of the given expression.
*
* @param expression a boolean expression
* @param errorCode execution error code
* @throws ExecutionException if {@code expression} is false
*/
public static void checkExecution(boolean expression, byte errorCode) {
if (!expression) {
throw new ExecutionException(errorCode);
}
}

/**
* Verifies the truth of the given expression.
*
* @param expression a boolean expression
* @param errorCode execution error code
* @param errorMessage execution error description to use if the check fails
* @throws ExecutionException if {@code expression} is false
*/
public static void checkExecution(boolean expression, byte errorCode,
@Nullable Object errorMessage) {
if (!expression) {
throw new ExecutionException(errorCode, String.valueOf(errorMessage));
}
}

/**
* Verifies the truth of the given expression.
*
* @param expression a boolean expression
* @param errorCode execution error code
* @param errorMessageTemplate execution error description template to use if the check fails.
* The template could have placeholders {@code %s} which will be replaced by arguments
* resolved by position
* @param errorMessageArgs arguments to be used in the template. Each argument will be converted
* to string using {@link String#valueOf(Object)}
* @throws ExecutionException if {@code expression} is false
*/
public static void checkExecution(boolean expression, byte errorCode,
@Nullable String errorMessageTemplate,
@Nullable Object... errorMessageArgs) {
if (!expression) {
throw new ExecutionException(errorCode,
lenientFormat(errorMessageTemplate, errorMessageArgs));
}
}

/**
* Verifies the truth of the given expression.
*
* <p>See {@link #checkExecution(boolean, byte, String, Object...)} for details.
*/
public static void checkExecution(boolean expression, byte errorCode,
@Nullable String errorMessageTemplate,
int arg1) {
if (!expression) {
throw new ExecutionException(errorCode, lenientFormat(errorMessageTemplate, arg1));
}
}

/**
* Verifies the truth of the given expression.
*
* <p>See {@link #checkExecution(boolean, byte, String, Object...)} for details.
*/
public static void checkExecution(boolean expression, byte errorCode,
@Nullable String errorMessageTemplate,
int arg1, int arg2) {
if (!expression) {
throw new ExecutionException(errorCode, lenientFormat(errorMessageTemplate, arg1, arg2));
}
}

/**
* Verifies the truth of the given expression.
*
* <p>See {@link #checkExecution(boolean, byte, String, Object...)} for details.
*/
public static void checkExecution(boolean expression, byte errorCode,
@Nullable String errorMessageTemplate,
int arg1, long arg2) {
if (!expression) {
throw new ExecutionException(errorCode, lenientFormat(errorMessageTemplate, arg1, arg2));
}
}

/**
* Verifies the truth of the given expression.
*
* <p>See {@link #checkExecution(boolean, byte, String, Object...)} for details.
*/
public static void checkExecution(boolean expression, byte errorCode,
@Nullable String errorMessageTemplate,
int arg1, @Nullable Object arg2) {
if (!expression) {
throw new ExecutionException(errorCode, lenientFormat(errorMessageTemplate, arg1, arg2));
}
}

/**
* Verifies the truth of the given expression.
*
* <p>See {@link #checkExecution(boolean, byte, String, Object...)} for details.
*/
public static void checkExecution(boolean expression, byte errorCode,
@Nullable String errorMessageTemplate,
long arg1) {
if (!expression) {
throw new ExecutionException(errorCode, lenientFormat(errorMessageTemplate, arg1));
}
}

/**
* Verifies the truth of the given expression.
*
* <p>See {@link #checkExecution(boolean, byte, String, Object...)} for details.
*/
public static void checkExecution(boolean expression, byte errorCode,
@Nullable String errorMessageTemplate,
long arg1, int arg2) {
if (!expression) {
throw new ExecutionException(errorCode, lenientFormat(errorMessageTemplate, arg1, arg2));
}
}

/**
* Verifies the truth of the given expression.
*
* <p>See {@link #checkExecution(boolean, byte, String, Object...)} for details.
*/
public static void checkExecution(boolean expression, byte errorCode,
@Nullable String errorMessageTemplate,
long arg1, long arg2) {
if (!expression) {
throw new ExecutionException(errorCode, lenientFormat(errorMessageTemplate, arg1, arg2));
}
}

/**
* Verifies the truth of the given expression.
*
* <p>See {@link #checkExecution(boolean, byte, String, Object...)} for details.
*/
public static void checkExecution(boolean expression, byte errorCode,
@Nullable String errorMessageTemplate,
long arg1, @Nullable Object arg2) {
if (!expression) {
throw new ExecutionException(errorCode, lenientFormat(errorMessageTemplate, arg1, arg2));
}
}

/**
* Verifies the truth of the given expression.
*
* <p>See {@link #checkExecution(boolean, byte, String, Object...)} for details.
*/
public static void checkExecution(boolean expression, byte errorCode,
@Nullable String errorMessageTemplate,
@Nullable Object arg1) {
if (!expression) {
throw new ExecutionException(errorCode, lenientFormat(errorMessageTemplate, arg1));
}
}

/**
* Verifies the truth of the given expression.
*
* <p>See {@link #checkExecution(boolean, byte, String, Object...)} for details.
*/
public static void checkExecution(boolean expression, byte errorCode,
@Nullable String errorMessageTemplate,
@Nullable Object arg1, int arg2) {
if (!expression) {
throw new ExecutionException(errorCode, lenientFormat(errorMessageTemplate, arg1, arg2));
}
}

/**
* Verifies the truth of the given expression.
*
* <p>See {@link #checkExecution(boolean, byte, String, Object...)} for details.
*/
public static void checkExecution(boolean expression, byte errorCode,
@Nullable String errorMessageTemplate,
@Nullable Object arg1, long arg2) {
if (!expression) {
throw new ExecutionException(errorCode, lenientFormat(errorMessageTemplate, arg1, arg2));
}
}

/**
* Verifies the truth of the given expression.
*
* <p>See {@link #checkExecution(boolean, byte, String, Object...)} for details.
*/
public static void checkExecution(boolean expression, byte errorCode,
@Nullable String errorMessageTemplate,
@Nullable Object arg1, @Nullable Object arg2) {
if (!expression) {
throw new ExecutionException(errorCode, lenientFormat(errorMessageTemplate, arg1, arg2));
}
}

/**
* Verifies the truth of the given expression.
*
* <p>See {@link #checkExecution(boolean, byte, String, Object...)} for details.
*/
public static void checkExecution(boolean expression, byte errorCode,
@Nullable String errorMessageTemplate,
@Nullable Object arg1, @Nullable Object arg2, @Nullable Object arg3) {
if (!expression) {
throw new ExecutionException(errorCode,
lenientFormat(errorMessageTemplate, arg1, arg2, arg3));
}
}

private ExecutionPreconditions() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright 2020 The Exonum Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.exonum.binding.core.transaction;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;

import org.junit.jupiter.api.Test;

class ExecutionPreconditionsTest {
private static final byte TEST_ERROR_CODE = 1;

@Test
void trueConditionDoesNothing() {
ExecutionPreconditions.checkExecution(true, TEST_ERROR_CODE);
}

@Test
void errorCodeIsPresent() {
ExecutionException e = assertThrows(ExecutionException.class,
() -> ExecutionPreconditions.checkExecution(false, TEST_ERROR_CODE));

assertThat(e.getErrorCode()).isEqualTo(TEST_ERROR_CODE);
}

@Test
void errorDescriptionIsPresent() {
String description = "evil error";
ExecutionException e = assertThrows(ExecutionException.class,
() -> ExecutionPreconditions.checkExecution(false, TEST_ERROR_CODE, description));

assertThat(e.getErrorCode()).isEqualTo(TEST_ERROR_CODE);
assertThat(e).hasMessage(description);
}

@Test
void nullableDescription() {
ExecutionException e = assertThrows(ExecutionException.class,
() -> ExecutionPreconditions.checkExecution(false, TEST_ERROR_CODE, null));

assertThat(e).hasMessage("null");
}

@Test
void errorDescriptionFormat() {
int p1 = 10;
int p2 = 20;

ExecutionException e = assertThrows(ExecutionException.class,
() -> ExecutionPreconditions.checkExecution(p1 == p2, TEST_ERROR_CODE, "%s != %s", p1, p2));

assertThat(e).hasMessage("10 != 20");
}
}
Loading