CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-apache-flink--flink-architecture-tests-base

Base library for Apache Flink architecture tests that provides common ArchUnit extensions and utilities for validating architectural constraints in both production and test code

Pending
Overview
Eval results
Files

method-validation.mddocs/

Method Validation

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.

Capabilities

Generic Predicate Fulfillment

Generic condition to check fulfillment of any predicate for elements with names.

/**
 * Generic condition to check fulfillment of a predicate
 * @param predicate The predicate that items must satisfy
 * @return ArchCondition that validates items against the predicate
 */
public static <T extends HasName> ArchCondition<T> fulfill(DescribedPredicate<T> predicate);

Usage Example:

import static org.apache.flink.architecture.common.Conditions.fulfill;
import com.tngtech.archunit.base.DescribedPredicate;

// Ensure all public methods follow naming convention
DescribedPredicate<JavaMethod> followsNamingConvention = 
    method -> method.getName().matches("^[a-z][a-zA-Z0-9]*$");

ArchRule methodNamingRule = methods()
    .that().arePublic()
    .should(fulfill(followsNamingConvention));

methodNamingRule.check(classes);

Comprehensive Type Analysis

Tests all leaf types used by a method (arguments, return type, and exceptions) against a given predicate.

/**
 * Tests leaf types of method arguments, return types, and exceptions
 * @param typePredicate Predicate to test against all leaf types
 * @return ArchCondition that validates all method leaf types
 */
public static ArchCondition<JavaMethod> haveLeafTypes(
    DescribedPredicate<JavaClass> typePredicate);

Usage Example:

import static org.apache.flink.architecture.common.Conditions.haveLeafTypes;
import static org.apache.flink.architecture.common.SourcePredicates.areJavaClasses;

// Ensure all method types are from allowed packages
DescribedPredicate<JavaClass> allowedTypes = areJavaClasses().and(
    clazz -> clazz.getPackageName().startsWith("org.apache.flink") ||
             clazz.getPackageName().startsWith("java.") ||
             clazz.getPackageName().startsWith("javax.")
);

ArchRule apiTypesRule = methods()
    .that().arePublic()
    .and().areNotConstructors()
    .should(haveLeafTypes(allowedTypes));

Return Type Analysis

Tests leaf return types of methods against a given predicate.

/**
 * Tests leaf return types of a method against the given predicate
 * @param typePredicate Predicate to test against return type leaf types
 * @return ArchCondition that validates method return types
 */
public static ArchCondition<JavaMethod> haveLeafReturnTypes(
    DescribedPredicate<JavaClass> typePredicate);

Usage Example:

import static org.apache.flink.architecture.common.Conditions.haveLeafReturnTypes;

// Ensure API methods don't return internal implementation types
DescribedPredicate<JavaClass> notInternalTypes = 
    clazz -> !clazz.getPackageName().contains(".internal.");

ArchRule publicApiReturnTypesRule = methods()
    .that().arePublic()
    .and().areDeclaredInClassesThat().haveSimpleNameEndingWith("API")
    .should(haveLeafReturnTypes(notInternalTypes));

Argument Type Analysis

Tests leaf argument types of methods against a given predicate.

/**
 * Tests leaf argument types of a method against the given predicate
 * @param typePredicate Predicate to test against argument type leaf types
 * @return ArchCondition that validates method argument types
 */
public static ArchCondition<JavaMethod> haveLeafArgumentTypes(
    DescribedPredicate<JavaClass> typePredicate);

Usage Example:

import static org.apache.flink.architecture.common.Conditions.haveLeafArgumentTypes;

// Ensure constructor arguments are serializable for certain classes
DescribedPredicate<JavaClass> isSerializable = 
    clazz -> clazz.isAssignableTo(java.io.Serializable.class) ||
             clazz.isPrimitive() ||
             clazz.getPackageName().startsWith("java.lang");

ArchRule serializableConstructorArgs = constructors()
    .that().areDeclaredInClassesThat().implement(Serializable.class)
    .should(haveLeafArgumentTypes(isSerializable));

Exception Type Analysis

Tests leaf exception types declared by methods against a given predicate.

/**
 * Tests leaf exception types of a method against the given predicate
 * @param typePredicate Predicate to test against exception type leaf types
 * @return ArchCondition that validates method exception types
 */
public static ArchCondition<JavaMethod> haveLeafExceptionTypes(
    DescribedPredicate<JavaClass> typePredicate);

Usage Example:

import static org.apache.flink.architecture.common.Conditions.haveLeafExceptionTypes;

// Ensure public API methods only throw documented exception types
DescribedPredicate<JavaClass> allowedExceptions = 
    clazz -> clazz.isAssignableTo(RuntimeException.class) ||
             clazz.getName().equals("java.io.IOException") ||
             clazz.getPackageName().startsWith("org.apache.flink.api.common.exceptions");

ArchRule apiExceptionTypesRule = methods()
    .that().arePublic()
    .and().areDeclaredInClassesThat().areAnnotatedWith(PublicEvolving.class)
    .should(haveLeafExceptionTypes(allowedExceptions));

Understanding Leaf Types

The leaf type analysis system recursively analyzes complex type structures:

Leaf Type Rules

  • Array types: Analyzes the base component type (e.g., String[]String)
  • Generic types: Analyzes both the raw type and all type arguments (e.g., List<Map<String, Integer>>List, Map, String, Integer)
  • Simple types: Analyzes the type itself

Complex Type Examples

// Method signature:
public CompletableFuture<List<ProcessingResult<String>>> processData(
    Map<String, Configuration> configs,
    Optional<ExecutionEnvironment> env
) throws ValidationException, ProcessingException

// Leaf types analyzed:
// Return type: CompletableFuture, List, ProcessingResult, String
// Arguments: Map, String, Configuration, Optional, ExecutionEnvironment  
// Exceptions: ValidationException, ProcessingException

Advanced Usage Patterns

Combining Multiple Conditions

// Comprehensive API validation
DescribedPredicate<JavaClass> apiCompliantTypes = 
    areJavaClasses().and(
        clazz -> clazz.getPackageName().startsWith("org.apache.flink.api") ||
                 clazz.getPackageName().startsWith("java.") ||
                 clazz.isPrimitive()
    );

ArchRule comprehensiveApiRule = methods()
    .that().arePublic()
    .and().areDeclaredInClassesThat().haveSimpleNameEndingWith("API")
    .should(haveLeafTypes(apiCompliantTypes))
    .andShould().haveRawReturnType(not(Object.class))
    .andShould().notHaveRawParameterTypes(Object.class);

Type Constraint Validation

// Ensure streaming API methods use streaming types
DescribedPredicate<JavaClass> streamingTypes = 
    clazz -> clazz.getPackageName().contains("streaming") ||
             clazz.getPackageName().startsWith("java.util.concurrent") ||
             clazz.isPrimitive();

ArchRule streamingApiTypesRule = methods()
    .that().arePublic()
    .and().areDeclaredInClassesThat().haveNameMatching(".*Stream.*")
    .should(haveLeafReturnTypes(streamingTypes))
    .andShould(haveLeafArgumentTypes(streamingTypes));

Exception Handling Validation

// Validate that service methods handle exceptions properly
DescribedPredicate<JavaClass> serviceExceptions = 
    clazz -> clazz.isAssignableTo(ServiceException.class) ||
             clazz.isAssignableTo(RuntimeException.class);

ArchRule serviceExceptionRule = methods()
    .that().arePublic()
    .and().areDeclaredInClassesThat().haveSimpleNameEndingWith("Service")
    .should(haveLeafExceptionTypes(serviceExceptions));

Design Principles

  • Deep Analysis: Recursively analyzes all type components, not just surface-level types
  • Java-Only Focus: Works exclusively with Java classes, filtering out Scala and other JVM languages
  • Composability: Conditions can be combined with other ArchUnit conditions using .and() and .or()
  • Performance: Efficient analysis suitable for large codebases with complex type hierarchies
  • Architectural Validation: Designed specifically for enforcing architectural constraints on method signatures

Install with Tessl CLI

npx tessl i tessl/maven-org-apache-flink--flink-architecture-tests-base

docs

field-analysis.md

general-predicates.md

import-control.md

index.md

java-only-rules.md

method-validation.md

source-predicates.md

tile.json