or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

field-analysis.mdgeneral-predicates.mdimport-control.mdindex.mdjava-only-rules.mdmethod-validation.mdsource-predicates.md

method-validation.mddocs/

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