CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-reflections--reflections

Java runtime metadata analysis library that enables reverse querying of classpath metadata for type system introspection

Pending
Overview
Eval results
Files

serialization.mddocs/

Serialization

Serialization capabilities for saving and loading scanned metadata in multiple formats including XML, JSON, and Java code generation. This enables persistent storage and sharing of metadata across different application runs and environments.

Capabilities

Serializer Interface

Base contract for all serialization implementations providing read and write operations.

/**
 * Contract for serializing/deserializing Reflections metadata
 */
interface Serializer {
    /** Read Reflections metadata from input stream */
    Reflections read(InputStream inputStream);
    
    /** Save Reflections metadata to file */
    File save(Reflections reflections, String filename);
    
    /** Prepare file with parent directories (static utility) */
    static File prepareFile(String filename);
}

XML Serialization

XML format serialization for human-readable and widely compatible metadata storage.

/**
 * XML serialization format implementation
 */
class XmlSerializer implements Serializer {
    /** Default constructor */
    XmlSerializer();
    
    /** Read XML metadata from input stream */
    Reflections read(InputStream inputStream);
    
    /** Save metadata as XML file */
    File save(Reflections reflections, String filename);
}

JSON Serialization

JSON format serialization for lightweight and web-friendly metadata storage.

/**
 * JSON serialization format implementation
 */
class JsonSerializer implements Serializer {
    /** Default constructor */
    JsonSerializer();
    
    /** Read JSON metadata from input stream */
    Reflections read(InputStream inputStream);
    
    /** Save metadata as JSON file */
    File save(Reflections reflections, String filename);
}

Java Code Generation

Java source code generation for type-safe compile-time access to metadata.

/**
 * Java source code generation for type-safe access
 */
class JavaCodeSerializer implements Serializer {
    /** Default constructor */
    JavaCodeSerializer();
    
    /** Not supported - throws UnsupportedOperationException */
    Reflections read(InputStream inputStream);
    
    /** Generate Java interface source code with type-safe metadata access methods */
    File save(Reflections reflections, String name);
}

Reflections Serialization Methods

Built-in serialization methods in the main Reflections class for convenient saving and loading.

/**
 * Built-in serialization methods in Reflections class
 */
class Reflections {
    /** Save metadata to XML file (default serializer) */
    File save(String filename);
    
    /** Save metadata to file using specified serializer */
    File save(String filename, Serializer serializer);
    
    /** Collect metadata from input stream using specified serializer */
    Reflections collect(InputStream inputStream, Serializer serializer);
    
    /** Collect metadata from file using specified serializer */
    Reflections collect(File file, Serializer serializer);
}

Static Collection Methods

Static methods for collecting pre-saved metadata from various sources and locations.

/**
 * Static methods for collecting saved metadata
 */
class Reflections {
    /** Collect saved XML metadata from META-INF/reflections/ with default filter */
    static Reflections collect();
    
    /** Collect saved metadata with custom package prefix and resource name filter */
    static Reflections collect(String packagePrefix, Predicate<String> resourceNameFilter);
    
    /** Collect saved metadata with custom serializer */
    static Reflections collect(String packagePrefix, Predicate<String> resourceNameFilter, Serializer serializer);
}

Usage Examples

Basic XML Serialization

import org.reflections.Reflections;
import org.reflections.serializers.XmlSerializer;

// Create and configure Reflections
Reflections reflections = new Reflections("com.mycompany");

// Save to XML using default serializer
File xmlFile = reflections.save("target/reflections-metadata.xml");

// Save to XML using explicit serializer
File explicitXmlFile = reflections.save("target/metadata.xml", new XmlSerializer());

// Load from XML
Reflections loadedReflections = new XmlSerializer().read(new FileInputStream(xmlFile));

// Collect from file
Reflections collected = new Reflections().collect(xmlFile, new XmlSerializer());

JSON Serialization

import org.reflections.serializers.JsonSerializer;
import java.io.FileInputStream;

// Save to JSON
JsonSerializer jsonSerializer = new JsonSerializer();
File jsonFile = reflections.save("target/reflections-metadata.json", jsonSerializer);

// Load from JSON
Reflections fromJson = jsonSerializer.read(new FileInputStream(jsonFile));

// Merge JSON data with existing Reflections
Reflections existing = new Reflections("com.mycompany.core");
Reflections merged = existing.collect(jsonFile, jsonSerializer);

Java Code Generation

import org.reflections.serializers.JavaCodeSerializer;

// Generate Java interface with type-safe access methods
JavaCodeSerializer codeSerializer = new JavaCodeSerializer();
File javaFile = reflections.save("src/generated/java/ReflectionsMetadata", codeSerializer);

// This generates a Java interface like:
// public interface ReflectionsMetadata {
//     Set<String> getSubTypesOf_com_mycompany_Service();
//     Set<String> getTypesAnnotatedWith_org_springframework_stereotype_Component();
//     // ... other generated methods
// }

// Note: JavaCodeSerializer.read() is not supported and throws UnsupportedOperationException

Collecting Pre-saved Metadata

import org.reflections.util.FilterBuilder;

// Collect all saved metadata from META-INF/reflections/
Reflections collected = Reflections.collect();

// Collect with package prefix filter
Reflections packageFiltered = Reflections.collect("com.mycompany", 
    resourceName -> resourceName.contains("reflections"));

// Collect with custom resource filter
Reflections customFiltered = Reflections.collect("", 
    new FilterBuilder().includePattern(".*-reflections\\.xml")::test);

// Collect using different serializer
Reflections jsonCollected = Reflections.collect("com.mycompany", 
    resourceName -> resourceName.endsWith(".json"), 
    new JsonSerializer());

Build-time Metadata Generation

// Example build-time metadata generation and collection pattern
public class MetadataGenerator {
    public static void main(String[] args) {
        // Generate metadata during build
        Reflections buildTimeReflections = new Reflections(new ConfigurationBuilder()
            .forPackages("com.mycompany")
            .setScanners(Scanners.values())
            .filterInputsBy(new FilterBuilder()
                .includePackage("com.mycompany")
                .excludePackage("com.mycompany.test")));
        
        // Save to build output directory
        buildTimeReflections.save("target/classes/META-INF/reflections/myapp-reflections.xml");
        
        // Also generate JSON version
        buildTimeReflections.save("target/classes/META-INF/reflections/myapp-reflections.json", 
            new JsonSerializer());
        
        // Generate type-safe access code
        buildTimeReflections.save("src/generated/java/com/mycompany/ReflectionsMetadata", 
            new JavaCodeSerializer());
    }
}

// Runtime collection (much faster than scanning)
public class RuntimeUsage {
    public void usePreGeneratedMetadata() {
        // Collect pre-generated metadata at runtime
        Reflections runtime = Reflections.collect("META-INF/reflections/", 
            name -> name.contains("myapp-reflections"));
        
        // Use normally - no scanning overhead
        Set<Class<?>> services = runtime.get(SubTypes.of(Service.class).asClass());
    }
}

Serialization Configuration and Customization

// Custom serialization workflow
public class CustomSerializationWorkflow {
    
    public void saveMultipleFormats(Reflections reflections, String baseName) {
        // Save in multiple formats
        reflections.save(baseName + ".xml", new XmlSerializer());
        reflections.save(baseName + ".json", new JsonSerializer());
        
        try {
            reflections.save(baseName + ".java", new JavaCodeSerializer());
        } catch (UnsupportedOperationException e) {
            // Handle JavaCodeSerializer read limitation
            System.out.println("Java code generation completed, read not supported");
        }
    }
    
    public Reflections loadFromMultipleSources() {
        Reflections combined = new Reflections();
        
        // Load and merge from different sources
        try {
            combined.merge(new XmlSerializer().read(
                new FileInputStream("metadata1.xml")));
        } catch (FileNotFoundException e) {
            // Handle missing file
        }
        
        try {
            combined.merge(new JsonSerializer().read(
                new FileInputStream("metadata2.json")));
        } catch (FileNotFoundException e) {
            // Handle missing file
        }
        
        return combined;
    }
}

Performance Optimization with Serialization

// Optimized metadata loading pattern
public class OptimizedMetadataLoading {
    private static final String METADATA_LOCATION = "META-INF/reflections/";
    private static Reflections cachedReflections;
    
    public static synchronized Reflections getReflections() {
        if (cachedReflections == null) {
            // Try to load from pre-saved metadata first (fast)
            try {
                cachedReflections = Reflections.collect(METADATA_LOCATION,
                    name -> name.endsWith("-reflections.xml"));
                
                if (!cachedReflections.getStore().isEmpty()) {
                    return cachedReflections;
                }
            } catch (Exception e) {
                // Fall back to scanning if collection fails
            }
            
            // Fall back to runtime scanning (slower)
            cachedReflections = new Reflections(new ConfigurationBuilder()
                .forPackages("com.mycompany")
                .setScanners(Scanners.SubTypes, Scanners.TypesAnnotated));
            
            // Save for next time
            try {
                cachedReflections.save("target/generated-reflections.xml");
            } catch (Exception e) {
                // Ignore save errors
            }
        }
        
        return cachedReflections;
    }
}

Serialization in Testing

// Testing with serialized metadata
public class SerializationTest {
    
    @Test
    public void testMetadataSerialization() throws IOException {
        // Create test metadata
        Reflections original = new Reflections("com.mycompany.test");
        
        // Save to temporary file
        File tempFile = File.createTempFile("test-reflections", ".xml");
        original.save(tempFile.getAbsolutePath());
        
        // Load and compare
        Reflections loaded = new XmlSerializer().read(new FileInputStream(tempFile));
        
        // Verify metadata integrity
        assertEquals(original.getStore().size(), loaded.getStore().size());
        assertEquals(original.getSubTypesOf(TestInterface.class), 
                    loaded.getSubTypesOf(TestInterface.class));
        
        // Cleanup
        tempFile.delete();
    }
    
    @Test
    public void testJsonSerialization() throws IOException {
        Reflections original = new Reflections("com.mycompany.test");
        
        // Test JSON serialization round-trip
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        new JsonSerializer().save(original, outputStream.toString());
        
        ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
        Reflections loaded = new JsonSerializer().read(inputStream);
        
        // Verify equality
        assertEquals(original.getStore(), loaded.getStore());
    }
}

Error Handling in Serialization

// Robust serialization with error handling
public class RobustSerialization {
    
    public boolean saveWithRetry(Reflections reflections, String filename, int maxRetries) {
        for (int i = 0; i < maxRetries; i++) {
            try {
                reflections.save(filename);
                return true;
            } catch (Exception e) {
                System.err.println("Save attempt " + (i + 1) + " failed: " + e.getMessage());
                if (i == maxRetries - 1) {
                    e.printStackTrace();
                }
            }
        }
        return false;
    }
    
    public Reflections loadWithFallback(String primaryFile, String fallbackFile) {
        // Try primary file first
        try {
            return new XmlSerializer().read(new FileInputStream(primaryFile));
        } catch (Exception e) {
            System.err.println("Failed to load primary metadata: " + e.getMessage());
        }
        
        // Try fallback file
        try {
            return new XmlSerializer().read(new FileInputStream(fallbackFile));
        } catch (Exception e) {
            System.err.println("Failed to load fallback metadata: " + e.getMessage());
        }
        
        // Fall back to runtime scanning
        System.err.println("Falling back to runtime scanning");
        return new Reflections("com.mycompany");
    }
    
    public void validateSerializedMetadata(Reflections reflections) throws IllegalStateException {
        Store store = reflections.getStore();
        
        if (store.isEmpty()) {
            throw new IllegalStateException("Loaded metadata is empty");
        }
        
        // Validate expected scanners are present
        if (!store.containsKey("SubTypes")) {
            throw new IllegalStateException("SubTypes scanner data missing");
        }
        
        if (!store.containsKey("TypesAnnotated")) {
            throw new IllegalStateException("TypesAnnotated scanner data missing");
        }
        
        // Validate data integrity
        Map<String, Set<String>> subTypes = store.get("SubTypes");
        if (subTypes.isEmpty()) {
            System.err.println("Warning: No SubTypes data found");
        }
    }
}

Serialization Best Practices

Format Selection

  • XML: Best for human readability, debugging, and integration with build tools
  • JSON: Best for web applications, RESTful APIs, and lightweight storage
  • Java Code: Best for compile-time type safety and eliminating runtime serialization overhead

Performance Considerations

  • Use serialization for build-time metadata generation to avoid runtime scanning overhead
  • Cache serialized metadata and check timestamps for invalidation
  • Use compressed formats for large metadata sets
  • Consider splitting large metadata into multiple files by package or functionality

Build Integration

  • Generate metadata during build process and include in application artifacts
  • Use Maven/Gradle plugins to automate metadata generation
  • Store metadata in classpath locations for automatic discovery
  • Version metadata files alongside application versions

Error Recovery

  • Always provide fallback to runtime scanning if serialized metadata is corrupted or missing
  • Validate serialized metadata integrity before use
  • Use multiple serialization formats for redundancy in critical applications
  • Implement retry mechanisms for network-based metadata loading

Types

/**
 * Exception thrown during serialization operations
 */
class ReflectionsException extends RuntimeException {
    ReflectionsException(String message);
    ReflectionsException(String message, Throwable cause);
    ReflectionsException(Throwable cause);
}

/**
 * File utility for serialization operations
 */
interface Serializer {
    /** Static utility to prepare file with parent directories */
    static File prepareFile(String filename) {
        File file = new File(filename);
        File parent = file.getAbsoluteFile().getParentFile();
        if (parent != null && !parent.exists()) {
            parent.mkdirs();
        }
        return file;
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-org-reflections--reflections

docs

configuration.md

core-operations.md

functional-queries.md

index.md

legacy-api.md

scanners.md

serialization.md

utilities.md

tile.json