CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-apereo-cas--cas-server-core-util-api

Apereo CAS Core Utilities - A comprehensive utility library providing functional programming constructs, encryption utilities, configuration helpers, and core infrastructure components for the Central Authentication Service framework

Pending
Overview
Eval results
Files

serialization.mddocs/

Serialization

Object serialization utilities with support for various formats, compression, encryption, and Jackson integration for secure and efficient data persistence and transmission.

SerializationUtils

Core utility class providing comprehensive serialization operations with support for encryption and encoding.

@UtilityClass
public class SerializationUtils {
    
    // Basic serialization operations
    public static byte[] serialize(Serializable object);
    public static void serialize(Serializable object, OutputStream outputStream);
    
    // Basic deserialization operations  
    public static <T> T deserialize(byte[] inBytes, Class<T> clazz);
    public static <T> T deserialize(InputStream inputStream, Class<T> clazz);
    
    // Encrypted serialization operations
    public static byte[] serializeAndEncodeObject(EncodableCipher cipher, 
                                                 Serializable object, 
                                                 Object[] parameters);
    
    public static byte[] serializeAndEncodeObject(EncodableCipher cipher, 
                                                 Serializable object);
    
    // Encrypted deserialization operations
    public static <T extends Serializable> T decodeAndDeserializeObject(
        byte[] object, 
        Class<T> type, 
        DecodableCipher cipher, 
        Object[] parameters
    );
    
    public static <T extends Serializable> T decodeAndDeserializeObject(
        byte[] object, 
        Class<T> type, 
        DecodableCipher cipher
    );
    
    // Validation and safety operations
    public static <T extends Serializable> T deserializeAndCheckObject(
        byte[] object, 
        Class<T> type
    );
}

Usage Examples

Basic serialization operations:

@Service
public class CacheService {
    
    public void storeUserSession(String sessionId, UserSession session) {
        try {
            // Serialize user session
            byte[] serialized = SerializationUtils.serialize(session);
            
            // Store in cache/database
            cacheManager.put(sessionId, serialized);
            
        } catch (Exception e) {
            log.error("Failed to serialize user session", e);
            throw new SerializationException("Session storage failed", e);
        }
    }
    
    public Optional<UserSession> retrieveUserSession(String sessionId) {
        try {
            byte[] data = cacheManager.get(sessionId);
            if (data != null) {
                UserSession session = SerializationUtils.deserialize(data, UserSession.class);
                return Optional.of(session);
            }
            return Optional.empty();
            
        } catch (Exception e) {
            log.error("Failed to deserialize user session", e);
            return Optional.empty();
        }
    }
    
    public void persistToFile(Serializable object, Path filePath) {
        try (FileOutputStream fos = new FileOutputStream(filePath.toFile())) {
            SerializationUtils.serialize(object, fos);
        } catch (Exception e) {
            log.error("Failed to persist object to file", e);
            throw new SerializationException("File persistence failed", e);
        }
    }
    
    public <T> Optional<T> loadFromFile(Path filePath, Class<T> type) {
        try (FileInputStream fis = new FileInputStream(filePath.toFile())) {
            T object = SerializationUtils.deserialize(fis, type);
            return Optional.of(object);
        } catch (Exception e) {
            log.error("Failed to load object from file", e);
            return Optional.empty();
        }
    }
}

Encrypted serialization for sensitive data:

@Service
public class SecureDataService {
    
    private final CipherExecutor<Serializable, byte[]> cipher;
    
    public SecureDataService(CipherExecutor<Serializable, byte[]> cipher) {
        this.cipher = cipher;
    }
    
    public void storeSecureData(String key, SensitiveData data) {
        try {
            // Serialize and encrypt in one operation
            byte[] encrypted = SerializationUtils.serializeAndEncodeObject(cipher, data);
            
            // Store encrypted data
            secureStorage.store(key, encrypted);
            
            log.info("Securely stored data for key: {}", key);
            
        } catch (Exception e) {
            log.error("Failed to store secure data", e);
            throw new SecurityException("Secure storage failed", e);
        }
    }
    
    public Optional<SensitiveData> retrieveSecureData(String key) {
        try {
            byte[] encrypted = secureStorage.retrieve(key);
            if (encrypted != null) {
                // Decrypt and deserialize in one operation
                SensitiveData data = SerializationUtils.decodeAndDeserializeObject(
                    encrypted, 
                    SensitiveData.class, 
                    cipher
                );
                return Optional.of(data);
            }
            return Optional.empty();
            
        } catch (Exception e) {
            log.error("Failed to retrieve secure data", e);
            return Optional.empty();
        }
    }
    
    public void storeWithParameters(String key, ConfigurableData data, String[] params) {
        try {
            // Use cipher parameters for additional security context
            Object[] cipherParams = Arrays.stream(params)
                .map(String::getBytes)
                .toArray();
            
            byte[] encrypted = SerializationUtils.serializeAndEncodeObject(
                cipher, 
                data, 
                cipherParams
            );
            
            secureStorage.store(key, encrypted);
            
        } catch (Exception e) {
            log.error("Failed to store configurable data", e);
            throw new SecurityException("Parameterized storage failed", e);
        }
    }
}

Safe deserialization with validation:

@Component
public class SafeDeserializationService {
    
    private final Set<Class<?>> allowedClasses;
    
    public SafeDeserializationService() {
        // Whitelist of allowed classes for deserialization
        this.allowedClasses = Set.of(
            UserSession.class,
            AuthenticationToken.class,
            ServiceTicket.class,
            CacheEntry.class
        );
    }
    
    public <T extends Serializable> Optional<T> safeDeserialize(byte[] data, Class<T> type) {
        // Validate class is allowed
        if (!isAllowedClass(type)) {
            log.warn("Attempted to deserialize disallowed class: {}", type.getName());
            return Optional.empty();
        }
        
        try {
            // Use safe deserialization with validation
            T object = SerializationUtils.deserializeAndCheckObject(data, type);
            
            // Additional validation
            if (isValidObject(object)) {
                return Optional.of(object);
            } else {
                log.warn("Deserialized object failed validation: {}", type.getName());
                return Optional.empty();
            }
            
        } catch (Exception e) {
            log.error("Safe deserialization failed for type: {}", type.getName(), e);
            return Optional.empty();
        }
    }
    
    private boolean isAllowedClass(Class<?> clazz) {
        return allowedClasses.contains(clazz) || 
               allowedClasses.stream().anyMatch(allowed -> allowed.isAssignableFrom(clazz));
    }
    
    private boolean isValidObject(Object object) {
        // Implement business logic validation
        if (object instanceof UserSession session) {
            return session.getUserId() != null && session.getCreatedAt() != null;
        }
        
        if (object instanceof AuthenticationToken token) {
            return token.getToken() != null && token.getExpiresAt() > System.currentTimeMillis();
        }
        
        return true; // Default validation
    }
}

Jackson Integration

BaseJacksonSerializer

Abstract Jackson-based serializer providing customizable JSON serialization.

public abstract class BaseJacksonSerializer<T> {
    
    // Jackson ObjectMapper instance
    protected final ObjectMapper objectMapper;
    
    // Constructor
    protected BaseJacksonSerializer(ObjectMapper objectMapper);
    
    // Serialization methods
    public String serialize(T object);
    public byte[] serializeToBytes(T object);
    public void serializeToStream(T object, OutputStream outputStream);
    
    // Deserialization methods  
    public T deserialize(String json);
    public T deserialize(byte[] data);
    public T deserialize(InputStream inputStream);
    
    // Abstract methods for subclasses
    protected abstract Class<T> getObjectType();
    protected abstract void configureObjectMapper(ObjectMapper mapper);
}

JacksonObjectMapperFactory

Factory for creating and configuring Jackson ObjectMapper instances with CAS-specific settings.

public class JacksonObjectMapperFactory {
    
    // Factory methods
    public static ObjectMapper builder();
    public static ObjectMapper builder(boolean defaultTypingEnabled);
    
    // Configuration methods
    public static ObjectMapper configure(ObjectMapper mapper);
    public static ObjectMapper configureForCas(ObjectMapper mapper);
    
    // Specialized configurations
    public static ObjectMapper createForTickets();
    public static ObjectMapper createForServices();  
    public static ObjectMapper createForAuthentication();
    
    // Feature configuration
    public static ObjectMapper withFeature(ObjectMapper mapper, 
                                         SerializationFeature feature, 
                                         boolean enabled);
    
    public static ObjectMapper withFeature(ObjectMapper mapper, 
                                         DeserializationFeature feature, 
                                         boolean enabled);
}

Usage Examples

Custom Jackson serializer implementation:

@Component
public class UserSessionSerializer extends BaseJacksonSerializer<UserSession> {
    
    public UserSessionSerializer() {
        super(JacksonObjectMapperFactory.builder());
    }
    
    @Override
    protected Class<UserSession> getObjectType() {
        return UserSession.class;
    }
    
    @Override
    protected void configureObjectMapper(ObjectMapper mapper) {
        // Custom configuration for user sessions
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        
        // Custom date format
        mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"));
        
        // Include type information for polymorphic handling
        mapper.activateDefaultTyping(
            LaissezFaireSubTypeValidator.instance,
            ObjectMapper.DefaultTyping.NON_FINAL
        );
        
        // Custom modules
        mapper.registerModule(new JavaTimeModule());
        mapper.registerModule(new CasJacksonModule());
    }
}

Factory usage for different contexts:

@Configuration
public class SerializationConfiguration {
    
    @Bean("ticketObjectMapper")
    public ObjectMapper ticketObjectMapper() {
        ObjectMapper mapper = JacksonObjectMapperFactory.createForTickets();
        
        // Additional ticket-specific configuration
        mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        mapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
        
        return mapper;
    }
    
    @Bean("serviceObjectMapper")  
    public ObjectMapper serviceObjectMapper() {
        ObjectMapper mapper = JacksonObjectMapperFactory.createForServices();
        
        // Service registry specific settings
        mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        
        return mapper;
    }
    
    @Bean("authenticationObjectMapper")
    public ObjectMapper authenticationObjectMapper() {
        ObjectMapper mapper = JacksonObjectMapperFactory.createForAuthentication();
        
        // Security-focused configuration
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);
        mapper.configure(JsonParser.Feature.STRICT_DUPLICATE_DETECTION, true);
        
        return mapper;
    }
}

DefaultComponentSerializationPlan

Default serialization plan for component-based serialization strategies.

public class DefaultComponentSerializationPlan implements ComponentSerializationPlan {
    
    // Serialization strategy configuration
    private final Map<Class<?>, SerializationStrategy> strategies;
    
    // Constructor
    public DefaultComponentSerializationPlan();
    public DefaultComponentSerializationPlan(Map<Class<?>, SerializationStrategy> strategies);
    
    // Plan execution
    @Override
    public byte[] serialize(Object object);
    
    @Override  
    public <T> T deserialize(byte[] data, Class<T> type);
    
    // Strategy management
    public void registerStrategy(Class<?> type, SerializationStrategy strategy);
    public void removeStrategy(Class<?> type);
    public SerializationStrategy getStrategy(Class<?> type);
}

Usage Examples

Component serialization plan:

@Configuration
public class SerializationPlanConfiguration {
    
    @Bean
    public ComponentSerializationPlan casSerializationPlan() {
        Map<Class<?>, SerializationStrategy> strategies = new HashMap<>();
        
        // Different strategies for different types
        strategies.put(UserSession.class, new JacksonSerializationStrategy());
        strategies.put(ServiceTicket.class, new CompactBinaryStrategy());  
        strategies.put(AuthenticationToken.class, new EncryptedJsonStrategy());
        strategies.put(CacheEntry.class, new CompressedSerializationStrategy());
        
        DefaultComponentSerializationPlan plan = new DefaultComponentSerializationPlan(strategies);
        
        // Register additional strategies
        plan.registerStrategy(RegisteredService.class, new ServiceJsonStrategy());
        
        return plan;
    }
}

@Service
public class ComponentSerializationService {
    
    private final ComponentSerializationPlan serializationPlan;
    
    public byte[] serializeComponent(Object component) {
        return serializationPlan.serialize(component);
    }
    
    public <T> T deserializeComponent(byte[] data, Class<T> type) {
        return serializationPlan.deserialize(data, type);
    }
}

JacksonInjectableValueSupplier

Injectable value supplier for providing runtime values during Jackson deserialization.

public class JacksonInjectableValueSupplier extends InjectableValues {
    
    // Value providers
    private final Map<String, Supplier<Object>> valueSuppliers;
    private final ApplicationContext applicationContext;
    
    // Constructor
    public JacksonInjectableValueSupplier(ApplicationContext applicationContext);
    
    // Value injection
    @Override
    public Object findInjectableValue(Object valueId, 
                                    DeserializationContext ctxt, 
                                    BeanProperty forProperty, 
                                    Object beanInstance);
    
    // Supplier registration
    public void registerSupplier(String valueId, Supplier<Object> supplier);
    public void registerBean(String valueId, Class<?> beanType);
    public void registerConstant(String valueId, Object value);
}

Usage Examples

Injectable value configuration:

@Configuration
public class JacksonInjectableConfiguration {
    
    @Bean
    public JacksonInjectableValueSupplier injectableValueSupplier(ApplicationContext context) {
        JacksonInjectableValueSupplier supplier = new JacksonInjectableValueSupplier(context);
        
        // Register runtime suppliers
        supplier.registerSupplier("currentTime", System::currentTimeMillis);
        supplier.registerSupplier("serverId", () -> getServerId());
        supplier.registerSupplier("environment", () -> getEnvironment());
        
        // Register Spring beans for injection
        supplier.registerBean("userService", UserService.class);
        supplier.registerBean("cacheManager", CacheManager.class);
        
        // Register constants
        supplier.registerConstant("version", "7.2.4");
        supplier.registerConstant("deployment", "production");
        
        return supplier;
    }
    
    @Bean
    public ObjectMapper injectableObjectMapper(JacksonInjectableValueSupplier supplier) {
        ObjectMapper mapper = JacksonObjectMapperFactory.builder();
        mapper.setInjectableValues(supplier);
        return mapper;
    }
}

// Usage in domain objects
public class AuditableEntity {
    
    @JsonProperty
    private String id;
    
    @JacksonInject("currentTime")
    private Long createdAt;
    
    @JacksonInject("serverId") 
    private String serverId;
    
    @JacksonInject("userService")
    private transient UserService userService;
    
    // getters/setters
}

PatternJsonDeserializer

Pattern-based JSON deserializer for handling regular expressions and pattern objects.

public class PatternJsonDeserializer extends JsonDeserializer<Pattern> {
    
    @Override
    public Pattern deserialize(JsonParser parser, 
                             DeserializationContext context) throws IOException;
    
    // Pattern compilation options
    public Pattern deserializeWithFlags(JsonParser parser, 
                                      DeserializationContext context,
                                      int flags) throws IOException;
}

Complete Integration Example

@Service
@Slf4j
public class ComprehensiveSerializationService {
    
    private final ObjectMapper jacksonMapper;
    private final CipherExecutor<Serializable, byte[]> cipher;
    private final ComponentSerializationPlan serializationPlan;
    
    public ComprehensiveSerializationService(
            @Qualifier("casObjectMapper") ObjectMapper jacksonMapper,
            CipherExecutor<Serializable, byte[]> cipher,
            ComponentSerializationPlan serializationPlan) {
        
        this.jacksonMapper = jacksonMapper;
        this.cipher = cipher;
        this.serializationPlan = serializationPlan;
    }
    
    // JSON serialization for web APIs
    public String toJson(Object object) {
        try {
            return jacksonMapper.writeValueAsString(object);
        } catch (JsonProcessingException e) {
            log.error("JSON serialization failed", e);
            throw new SerializationException("JSON conversion failed", e);
        }
    }
    
    public <T> T fromJson(String json, Class<T> type) {
        try {
            return jacksonMapper.readValue(json, type);
        } catch (JsonProcessingException e) {
            log.error("JSON deserialization failed", e);
            throw new SerializationException("JSON parsing failed", e);
        }
    }
    
    // Binary serialization for caching
    public byte[] toBinary(Serializable object) {
        return SerializationUtils.serialize(object);
    }
    
    public <T> T fromBinary(byte[] data, Class<T> type) {
        return SerializationUtils.deserialize(data, type);
    }
    
    // Encrypted serialization for sensitive data
    public byte[] toEncryptedBinary(Serializable object) {
        return SerializationUtils.serializeAndEncodeObject(cipher, object);
    }
    
    public <T extends Serializable> T fromEncryptedBinary(byte[] data, Class<T> type) {
        return SerializationUtils.decodeAndDeserializeObject(data, type, cipher);
    }
    
    // Component-based serialization
    public byte[] serializeComponent(Object component) {
        return serializationPlan.serialize(component);
    }
    
    public <T> T deserializeComponent(byte[] data, Class<T> type) {
        return serializationPlan.deserialize(data, type);
    }
    
    // Hybrid operations combining multiple strategies
    public StorageEntry createStorageEntry(String key, Object data, StorageOptions options) {
        byte[] serializedData;
        
        if (options.isEncrypted()) {
            if (data instanceof Serializable serializable) {
                serializedData = toEncryptedBinary(serializable);
            } else {
                // Convert to JSON first, then encrypt
                String json = toJson(data);
                serializedData = toEncryptedBinary(json);
            }
        } else if (options.useComponentSerialization()) {
            serializedData = serializeComponent(data);
        } else if (options.isJsonFormat()) {
            String json = toJson(data);
            serializedData = json.getBytes(StandardCharsets.UTF_8);
        } else {
            if (data instanceof Serializable serializable) {
                serializedData = toBinary(serializable);
            } else {
                throw new IllegalArgumentException("Object must be Serializable for binary format");
            }
        }
        
        return new StorageEntry(key, serializedData, options);
    }
    
    public <T> T retrieveFromStorageEntry(StorageEntry entry, Class<T> type) {
        StorageOptions options = entry.getOptions();
        byte[] data = entry.getData();
        
        if (options.isEncrypted()) {
            if (String.class.equals(type)) {
                String decrypted = fromEncryptedBinary(data, String.class);
                return jacksonMapper.convertValue(decrypted, type);
            } else {
                return fromEncryptedBinary(data, type.asSubclass(Serializable.class));
            }
        } else if (options.useComponentSerialization()) {
            return deserializeComponent(data, type);
        } else if (options.isJsonFormat()) {
            String json = new String(data, StandardCharsets.UTF_8);
            return fromJson(json, type);
        } else {
            return fromBinary(data, type);
        }
    }
}

This serialization library provides comprehensive support for various serialization needs in CAS applications, from simple JSON conversion to encrypted binary storage with proper security and performance considerations.

Install with Tessl CLI

npx tessl i tessl/maven-org-apereo-cas--cas-server-core-util-api

docs

core-utilities.md

cryptography.md

functional-programming.md

generators.md

http-clients.md

index.md

jwt-utilities.md

serialization.md

specialized-utilities.md

spring-integration.md

text-processing.md

tile.json