0
# Method Validation
1
2
ArchUnit conditions for validating method signatures, including deep analysis of parameter types, return types, and exception types. This module provides powerful capabilities for ensuring architectural constraints on method-level APIs.
3
4
## Capabilities
5
6
### Generic Predicate Fulfillment
7
8
Generic condition to check fulfillment of any predicate for elements with names.
9
10
```java { .api }
11
/**
12
* Generic condition to check fulfillment of a predicate
13
* @param predicate The predicate that items must satisfy
14
* @return ArchCondition that validates items against the predicate
15
*/
16
public static <T extends HasName> ArchCondition<T> fulfill(DescribedPredicate<T> predicate);
17
```
18
19
**Usage Example:**
20
21
```java
22
import static org.apache.flink.architecture.common.Conditions.fulfill;
23
import com.tngtech.archunit.base.DescribedPredicate;
24
25
// Ensure all public methods follow naming convention
26
DescribedPredicate<JavaMethod> followsNamingConvention =
27
method -> method.getName().matches("^[a-z][a-zA-Z0-9]*$");
28
29
ArchRule methodNamingRule = methods()
30
.that().arePublic()
31
.should(fulfill(followsNamingConvention));
32
33
methodNamingRule.check(classes);
34
```
35
36
### Comprehensive Type Analysis
37
38
Tests all leaf types used by a method (arguments, return type, and exceptions) against a given predicate.
39
40
```java { .api }
41
/**
42
* Tests leaf types of method arguments, return types, and exceptions
43
* @param typePredicate Predicate to test against all leaf types
44
* @return ArchCondition that validates all method leaf types
45
*/
46
public static ArchCondition<JavaMethod> haveLeafTypes(
47
DescribedPredicate<JavaClass> typePredicate);
48
```
49
50
**Usage Example:**
51
52
```java
53
import static org.apache.flink.architecture.common.Conditions.haveLeafTypes;
54
import static org.apache.flink.architecture.common.SourcePredicates.areJavaClasses;
55
56
// Ensure all method types are from allowed packages
57
DescribedPredicate<JavaClass> allowedTypes = areJavaClasses().and(
58
clazz -> clazz.getPackageName().startsWith("org.apache.flink") ||
59
clazz.getPackageName().startsWith("java.") ||
60
clazz.getPackageName().startsWith("javax.")
61
);
62
63
ArchRule apiTypesRule = methods()
64
.that().arePublic()
65
.and().areNotConstructors()
66
.should(haveLeafTypes(allowedTypes));
67
```
68
69
### Return Type Analysis
70
71
Tests leaf return types of methods against a given predicate.
72
73
```java { .api }
74
/**
75
* Tests leaf return types of a method against the given predicate
76
* @param typePredicate Predicate to test against return type leaf types
77
* @return ArchCondition that validates method return types
78
*/
79
public static ArchCondition<JavaMethod> haveLeafReturnTypes(
80
DescribedPredicate<JavaClass> typePredicate);
81
```
82
83
**Usage Example:**
84
85
```java
86
import static org.apache.flink.architecture.common.Conditions.haveLeafReturnTypes;
87
88
// Ensure API methods don't return internal implementation types
89
DescribedPredicate<JavaClass> notInternalTypes =
90
clazz -> !clazz.getPackageName().contains(".internal.");
91
92
ArchRule publicApiReturnTypesRule = methods()
93
.that().arePublic()
94
.and().areDeclaredInClassesThat().haveSimpleNameEndingWith("API")
95
.should(haveLeafReturnTypes(notInternalTypes));
96
```
97
98
### Argument Type Analysis
99
100
Tests leaf argument types of methods against a given predicate.
101
102
```java { .api }
103
/**
104
* Tests leaf argument types of a method against the given predicate
105
* @param typePredicate Predicate to test against argument type leaf types
106
* @return ArchCondition that validates method argument types
107
*/
108
public static ArchCondition<JavaMethod> haveLeafArgumentTypes(
109
DescribedPredicate<JavaClass> typePredicate);
110
```
111
112
**Usage Example:**
113
114
```java
115
import static org.apache.flink.architecture.common.Conditions.haveLeafArgumentTypes;
116
117
// Ensure constructor arguments are serializable for certain classes
118
DescribedPredicate<JavaClass> isSerializable =
119
clazz -> clazz.isAssignableTo(java.io.Serializable.class) ||
120
clazz.isPrimitive() ||
121
clazz.getPackageName().startsWith("java.lang");
122
123
ArchRule serializableConstructorArgs = constructors()
124
.that().areDeclaredInClassesThat().implement(Serializable.class)
125
.should(haveLeafArgumentTypes(isSerializable));
126
```
127
128
### Exception Type Analysis
129
130
Tests leaf exception types declared by methods against a given predicate.
131
132
```java { .api }
133
/**
134
* Tests leaf exception types of a method against the given predicate
135
* @param typePredicate Predicate to test against exception type leaf types
136
* @return ArchCondition that validates method exception types
137
*/
138
public static ArchCondition<JavaMethod> haveLeafExceptionTypes(
139
DescribedPredicate<JavaClass> typePredicate);
140
```
141
142
**Usage Example:**
143
144
```java
145
import static org.apache.flink.architecture.common.Conditions.haveLeafExceptionTypes;
146
147
// Ensure public API methods only throw documented exception types
148
DescribedPredicate<JavaClass> allowedExceptions =
149
clazz -> clazz.isAssignableTo(RuntimeException.class) ||
150
clazz.getName().equals("java.io.IOException") ||
151
clazz.getPackageName().startsWith("org.apache.flink.api.common.exceptions");
152
153
ArchRule apiExceptionTypesRule = methods()
154
.that().arePublic()
155
.and().areDeclaredInClassesThat().areAnnotatedWith(PublicEvolving.class)
156
.should(haveLeafExceptionTypes(allowedExceptions));
157
```
158
159
## Understanding Leaf Types
160
161
The leaf type analysis system recursively analyzes complex type structures:
162
163
### Leaf Type Rules
164
165
- **Array types**: Analyzes the base component type (e.g., `String[]` → `String`)
166
- **Generic types**: Analyzes both the raw type and all type arguments (e.g., `List<Map<String, Integer>>` → `List`, `Map`, `String`, `Integer`)
167
- **Simple types**: Analyzes the type itself
168
169
### Complex Type Examples
170
171
```java
172
// Method signature:
173
public CompletableFuture<List<ProcessingResult<String>>> processData(
174
Map<String, Configuration> configs,
175
Optional<ExecutionEnvironment> env
176
) throws ValidationException, ProcessingException
177
178
// Leaf types analyzed:
179
// Return type: CompletableFuture, List, ProcessingResult, String
180
// Arguments: Map, String, Configuration, Optional, ExecutionEnvironment
181
// Exceptions: ValidationException, ProcessingException
182
```
183
184
## Advanced Usage Patterns
185
186
### Combining Multiple Conditions
187
188
```java
189
// Comprehensive API validation
190
DescribedPredicate<JavaClass> apiCompliantTypes =
191
areJavaClasses().and(
192
clazz -> clazz.getPackageName().startsWith("org.apache.flink.api") ||
193
clazz.getPackageName().startsWith("java.") ||
194
clazz.isPrimitive()
195
);
196
197
ArchRule comprehensiveApiRule = methods()
198
.that().arePublic()
199
.and().areDeclaredInClassesThat().haveSimpleNameEndingWith("API")
200
.should(haveLeafTypes(apiCompliantTypes))
201
.andShould().haveRawReturnType(not(Object.class))
202
.andShould().notHaveRawParameterTypes(Object.class);
203
```
204
205
### Type Constraint Validation
206
207
```java
208
// Ensure streaming API methods use streaming types
209
DescribedPredicate<JavaClass> streamingTypes =
210
clazz -> clazz.getPackageName().contains("streaming") ||
211
clazz.getPackageName().startsWith("java.util.concurrent") ||
212
clazz.isPrimitive();
213
214
ArchRule streamingApiTypesRule = methods()
215
.that().arePublic()
216
.and().areDeclaredInClassesThat().haveNameMatching(".*Stream.*")
217
.should(haveLeafReturnTypes(streamingTypes))
218
.andShould(haveLeafArgumentTypes(streamingTypes));
219
```
220
221
### Exception Handling Validation
222
223
```java
224
// Validate that service methods handle exceptions properly
225
DescribedPredicate<JavaClass> serviceExceptions =
226
clazz -> clazz.isAssignableTo(ServiceException.class) ||
227
clazz.isAssignableTo(RuntimeException.class);
228
229
ArchRule serviceExceptionRule = methods()
230
.that().arePublic()
231
.and().areDeclaredInClassesThat().haveSimpleNameEndingWith("Service")
232
.should(haveLeafExceptionTypes(serviceExceptions));
233
```
234
235
## Design Principles
236
237
- **Deep Analysis**: Recursively analyzes all type components, not just surface-level types
238
- **Java-Only Focus**: Works exclusively with Java classes, filtering out Scala and other JVM languages
239
- **Composability**: Conditions can be combined with other ArchUnit conditions using `.and()` and `.or()`
240
- **Performance**: Efficient analysis suitable for large codebases with complex type hierarchies
241
- **Architectural Validation**: Designed specifically for enforcing architectural constraints on method signatures