Skip to content

Commit

Permalink
feat: published
Browse files Browse the repository at this point in the history
  • Loading branch information
QwQ-dev committed Jan 25, 2025
1 parent 95ba067 commit b31175a
Show file tree
Hide file tree
Showing 9 changed files with 279 additions and 188 deletions.
128 changes: 76 additions & 52 deletions annotation/README.md
Original file line number Diff line number Diff line change
@@ -1,51 +1,58 @@
# 📚 Annotation Module

Based on the [Reflections](https://github.com/ronmamo/reflections) library, this module automates the scanning and processing of annotations, supporting multiple scenarios and multiple ClassLoaders for plugins or other modular environments.
Based on the [Reflections](https://github.com/ronmamo/reflections) library, this module automates the scanning and
processing of annotations, supporting multiple scenarios and multiple ClassLoaders for plugins or other modular
environments.

[![JDK](https://img.shields.io/badge/JDK-17%2B-blue.svg)](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html)
[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)

## 📋 Table of Contents

- [Annotation Module](#-annotation-module)
- [Table of Contents](#table-of-contents)
- [Key Features](#key-features)
- [Module Overview](#module-overview)
- [Getting Started](#getting-started)
- [Dependency](#dependency)
- [Core Concept](#core-concept)
- [Usage Example](#usage-example)
- [1. Define an Annotation](#1-define-an-annotation)
- [2. Create a Custom Annotation Processor](#2-create-a-custom-annotation-processor)
- [3. Integrate Within a Plugin/Class](#3-integrate-within-a-pluginclass)
- [How It Works](#how-it-works)
- [1. Multiple ClassLoaders](#1-multiple-classloaders)
- [2. AnnotationProcessingService](#2-annotationprocessingservice)
- [Detailed Architecture](#detailed-architecture)
- [Key Classes and Responsibilities](#key-classes-and-responsibilities)
- [Additional Notes](#additional-notes)
- [License](#license)
- [Table of Contents](#table-of-contents)
- [Key Features](#key-features)
- [Module Overview](#module-overview)
- [Getting Started](#getting-started)
- [Dependency](#dependency)
- [Core Concept](#core-concept)
- [Usage Example](#usage-example)
- [1. Define an Annotation](#1-define-an-annotation)
- [2. Create a Custom Annotation Processor](#2-create-a-custom-annotation-processor)
- [3. Integrate Within a Plugin/Class](#3-integrate-within-a-pluginclass)
- [How It Works](#how-it-works)
- [1. Multiple ClassLoaders](#1-multiple-classloaders)
- [2. AnnotationProcessingService](#2-annotationprocessingservice)
- [Detailed Architecture](#detailed-architecture)
- [Key Classes and Responsibilities](#key-classes-and-responsibilities)
- [Additional Notes](#additional-notes)
- [License](#license)

## Key Features

- 🔍 Automated annotation scanning using Reflections, configurable via package names or explicit URLs.
- 📚 Supports multiple ClassLoaders, which is especially useful for cross-plugin or multi-module environments.
- 🔍 Automated annotation scanning using Reflections, configurable via package names or explicit URLs.
- 📚 Supports multiple ClassLoaders, which is especially useful for cross-plugin or multi-module environments.
- 🔄 Simplifies boilerplate by allowing you to write custom processors for a whole category of annotated classes.

## Module Overview

This module uses the Reflections library to search for specified annotations within your codebase and pass each discovered class to your custom processor (implementing CustomAnnotationProcessor). In short:
This module uses the Reflections library to search for specified annotations within your codebase and pass each
discovered class to your custom processor (implementing CustomAnnotationProcessor). In short:

1. It can locate classes annotated with a target annotation across multiple package paths, JARs, or service URLs.
2. It assigns these classes to their appropriate processor, as identified by the @AnnotationProcessor meta-annotation.
3. It integrates with the Fairy IoC container; processors can be injected as singletons or instantiated via reflection, offering flexibility depending on the application environment.
1. It can locate classes annotated with a target annotation across multiple package paths, JARs, or service URLs.
2. It assigns these classes to their appropriate processor, as identified by the @AnnotationProcessor meta-annotation.
3. It integrates with the Fairy IoC container; processors can be injected as singletons or instantiated via reflection,
offering flexibility depending on the application environment.

This is particularly suited to environments such as BungeeCord/Spigot/Folia where it is common to scan multiple plugins for shared annotations, thus simplifying registration or initialization tasks.
This is particularly suited to environments such as BungeeCord/Spigot/Folia where it is common to scan multiple plugins
for shared annotations, thus simplifying registration or initialization tasks.

## Getting Started

### Dependency

Include this module's build artifact in your own project. The following example demonstrates how to reference it via a Gradle Kotlin DSL:
Include this module's build artifact in your own project. The following example demonstrates how to reference it via a
Gradle Kotlin DSL:

```kotlin
dependencies {
Expand All @@ -54,23 +61,26 @@ dependencies {
}
```

> Note: Adjust the setup as needed for your build system (e.g., Maven, Gradle) and your preferred repository arrangement.
> Note: Adjust the setup as needed for your build system (e.g., Maven, Gradle) and your preferred repository
> arrangement.
### Core Concept

You can think of this module as a "library book index":

- You have a collection of "books" (classes) containing specific "stickers" (annotations).
- You need to locate books with certain stickers (search for classes by annotation).
- You have a collection of "books" (classes) containing specific "stickers" (annotations).
- You need to locate books with certain stickers (search for classes by annotation).
- You then perform custom operations on these books (custom annotation processor logic).

AnnotationProcessingService coordinates the overall scanning, while each CustomAnnotationProcessor implementation defines how to handle classes that match a specific annotation.
AnnotationProcessingService coordinates the overall scanning, while each CustomAnnotationProcessor implementation
defines how to handle classes that match a specific annotation.

### Usage Example

#### 1. Define an Annotation

Below is an example of an annotation (SimplixSerializerSerializableAutoRegister) indicating classes that should be automatically registered for serialization:
Below is an example of an annotation (SimplixSerializerSerializableAutoRegister) indicating classes that should be
automatically registered for serialization:

```java
@Target(ElementType.TYPE)
Expand All @@ -81,7 +91,8 @@ public @interface SimplixSerializerSerializableAutoRegister {

#### 2. Create a Custom Annotation Processor

Use the @AnnotationProcessor meta-annotation to specify the target annotation for your processor, then implement the CustomAnnotationProcessor interface:
Use the @AnnotationProcessor meta-annotation to specify the target annotation for your processor, then implement the
CustomAnnotationProcessor interface:

```java
@AnnotationProcessor(SimplixSerializerSerializableAutoRegister.class)
Expand All @@ -99,7 +110,8 @@ public class SimplixSerializerSerializableAutoRegisterProcessor implements Custo
}
```

You can optionally override the before, after, or finallyAfter methods to execute additional logic before or after processing each class.
You can optionally override the before, after, or finallyAfter methods to execute additional logic before or after
processing each class.

#### 3. Integrate Within a Plugin/Class

Expand Down Expand Up @@ -129,49 +141,61 @@ public class Launcher extends Plugin {
}
```

When you need to scan multiple plugins or modules for the same annotation, simply supply each relevant ClassLoader in the method call.
When you need to scan multiple plugins or modules for the same annotation, simply supply each relevant ClassLoader in
the method call.

## How It Works

### 1. Multiple ClassLoaders

- Each plugin or module is typically managed by its own ClassLoader.
- To discover classes from another plugin/module, pass the associated ClassLoader(s).
- Each plugin or module is typically managed by its own ClassLoader.
- To discover classes from another plugin/module, pass the associated ClassLoader(s).
- AnnotationProcessingService can aggregate classes from all these loaders, scanning them in a single run.

### 2. AnnotationProcessingService

This service forms the core of the module's functionality:

1. Given packages or URLs, it uses Reflections to find classes with the specified annotations.
2. It checks each available processor (indicated by @AnnotationProcessor) and matches them to the correct annotation.
3. For each discovered class, it calls the processor's process and exception methods, and optionally before, after, and finallyAfter if implemented.
1. Given packages or URLs, it uses Reflections to find classes with the specified annotations.
2. It checks each available processor (indicated by @AnnotationProcessor) and matches them to the correct annotation.
3. For each discovered class, it calls the processor's process and exception methods, and optionally before, after, and
finallyAfter if implemented.

## Detailed Architecture

### Key Classes and Responsibilities

• AnnotationProcessingService
- Implements AnnotationProcessingServiceInterface to govern scanning and invocation of processors.
- Utilizes AnnotationScanner and ReflectUtil to locate annotated classes and manage processor instances.
• AnnotationProcessingService

- Implements AnnotationProcessingServiceInterface to govern scanning and invocation of processors.
- Utilizes AnnotationScanner and ReflectUtil to locate annotated classes and manage processor instances.

• AnnotationScanner

- Wraps Reflections usage, enabling you to locate annotated classes by package name or URL collection.

• CustomAnnotationProcessor

- Interface for creating custom processors; outlines how classes should be handled and how exceptions should be
processed.

• AnnotationScanner
- Wraps Reflections usage, enabling you to locate annotated classes by package name or URL collection.
@AnnotationProcessor

• CustomAnnotationProcessor
- Interface for creating custom processors; outlines how classes should be handled and how exceptions should be processed.
- Meta-annotation designating which annotation the processor targets.

@AnnotationProcessor
- Meta-annotation designating which annotation the processor targets.
• ReflectUtil

• ReflectUtil
- A utility class to combine multiple ClassLoader scanning paths (URLs), suitable for multi-package or multi-module contexts.
- A utility class to combine multiple ClassLoader scanning paths (URLs), suitable for multi-package or multi-module
contexts.

## Additional Notes

When you need to handle multiple annotations, you can create separate processors or incorporate logic for multiple annotations in a single processor. Ensure each processor class is annotated with @AnnotationProcessor, specifying its target annotation(s).
When you need to handle multiple annotations, you can create separate processors or incorporate logic for multiple
annotations in a single processor. Ensure each processor class is annotated with @AnnotationProcessor, specifying its
target annotation(s).

Furthermore, since Fairy IoC can automatically manage and inject class instances, you may choose between a singleton approach or reflection-based construction, accommodating different scopes as your application requires.
Furthermore, since Fairy IoC can automatically manage and inject class instances, you may choose between a singleton
approach or reflection-based construction, accommodating different scopes as your application requires.

## License

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import io.fairyproject.container.Containers;
import io.fairyproject.container.InjectableComponent;
import io.fairyproject.container.scope.InjectableScope;
import io.fairyproject.log.Log;
import net.legacy.library.annotation.util.AnnotationScanner;
import net.legacy.library.annotation.util.ReflectUtil;
Expand Down
65 changes: 42 additions & 23 deletions cache/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# 🚀 Cache Module

A powerful multi-level caching solution that leverages Caffeine and Redis, with functional programming support and automatic lock handling. This module is built for asynchronous, distributed, and multi-tier caching scenarios focused on flexible configurations and high performance.
A powerful multi-level caching solution that leverages Caffeine and Redis, with functional programming support and
automatic lock handling. This module is built for asynchronous, distributed, and multi-tier caching scenarios focused on
flexible configurations and high performance.

[![JDK](https://img.shields.io/badge/JDK-17%2B-blue.svg)](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html)
[![License](https://img.shields.io/badge/license-MIT-green.svg)](../LICENSE)
Expand All @@ -20,25 +22,31 @@ A powerful multi-level caching solution that leverages Caffeine and Redis, with
- [Introduction](#introduction)
- [Installation](#installation)
- [Usage](#usage)
- [Caffeine Cache (Synchronous and Asynchronous)](#caffeine-cache-synchronous-and-asynchronous)
- [Redis Cache](#redis-cache)
- [Custom Cache](#custom-cache)
- [Multi-Level Cache](#multi-level-cache)
- [Caffeine Cache (Synchronous and Asynchronous)](#caffeine-cache-synchronous-and-asynchronous)
- [Redis Cache](#redis-cache)
- [Custom Cache](#custom-cache)
- [Multi-Level Cache](#multi-level-cache)
- [Architecture](#architecture)
- [Core Classes](#core-classes)
- [Locking Mechanism](#locking-mechanism)
- [Core Classes](#core-classes)
- [Locking Mechanism](#locking-mechanism)
- [Contributing](#contributing)
- [License](#license)

## Introduction

The "cache" module provides a flexible, extensible caching solution suitable for a variety of scenarios, from single-node in-memory caches to distributed systems requiring shared state across multiple nodes. By combining different caching technologies (Caffeine for in-memory and Redis for remote caching), you can create multi-tier caching strategies that optimize both performance and data consistency.
The "cache" module provides a flexible, extensible caching solution suitable for a variety of scenarios, from
single-node in-memory caches to distributed systems requiring shared state across multiple nodes. By combining different
caching technologies (Caffeine for in-memory and Redis for remote caching), you can create multi-tier caching strategies
that optimize both performance and data consistency.

Key highlights include:

1. Standardized access methods across all cache implementations.
2. Lockable cache operations to handle concurrency, ensuring that only one thread computes or updates a cache entry at a time.
2. Lockable cache operations to handle concurrency, ensuring that only one thread computes or updates a cache entry at a
time.
3. Simple extension points enabling custom caches to integrate seamlessly.
4. Multiple layers of caches that can be composed to form a hierarchical cache (e.g., a local memory cache tier, plus a Redis tier).
4. Multiple layers of caches that can be composed to form a hierarchical cache (e.g., a local memory cache tier, plus a
Redis tier).

## Installation

Expand All @@ -54,11 +62,13 @@ Adjust according to your preferred build system and repository hosting.

## Usage

Below are common use cases and examples showcasing how to instantiate and use the caches. For more detailed or advanced scenarios, refer to individual class documentation within the source code.
Below are common use cases and examples showcasing how to instantiate and use the caches. For more detailed or advanced
scenarios, refer to individual class documentation within the source code.

### Caffeine Cache (Synchronous and Asynchronous)

Caffeine provides a high-performance, in-memory cache with excellent hit rates. You can create either synchronous or asynchronous cache services through the CacheServiceFactory.
Caffeine provides a high-performance, in-memory cache with excellent hit rates. You can create either synchronous or
asynchronous cache services through the CacheServiceFactory.

• Synchronous Example:

Expand Down Expand Up @@ -139,7 +149,8 @@ String computedValue = customCache.get(

### Multi-Level Cache

You can combine multiple cache layers (e.g., a fast local memory tier + a Redis tier) using FlexibleMultiLevelCacheService plus TieredCacheLevel objects. For instance:
You can combine multiple cache layers (e.g., a fast local memory tier + a Redis tier) using
FlexibleMultiLevelCacheService plus TieredCacheLevel objects. For instance:

```java
// Primary in-memory Caffeine cache
Expand Down Expand Up @@ -167,39 +178,47 @@ String multiValue = multiLevelCache.applyFunctionWithoutLock(
);
```

Multi-level configurations allow you to decide priorities, fallback strategies, and lock usage for each tier, ensuring you get the speed of local caching while retaining the consistency or scalability of remote caches.
Multi-level configurations allow you to decide priorities, fallback strategies, and lock usage for each tier, ensuring
you get the speed of local caching while retaining the consistency or scalability of remote caches.

## Architecture

### Core Classes

• CacheServiceInterface
- Defines essential get(...) methods for retrieving or computing values.
- Implemented by various cache service classes (RedisCacheService, CaffeineCacheService, etc.).

- Defines essential get(...) methods for retrieving or computing values.
- Implemented by various cache service classes (RedisCacheService, CaffeineCacheService, etc.).

• AbstractCacheService & AbstractLockableCache
- Provide shared logic, enabling locked and non-locked operations.
- Manage concurrency with Lock or Redisson RLock.

- Provide shared logic, enabling locked and non-locked operations.
- Manage concurrency with Lock or Redisson RLock.

• FlexibleMultiLevelCacheService
- Manages multiple caching tiers.
- Uses TieredCacheLevel objects to unify them under a single interface.

- Manages multiple caching tiers.
- Uses TieredCacheLevel objects to unify them under a single interface.

• RedisCacheService & RedisCacheServiceInterface
- Wrap RedissonClient for persistent/distributed caching.
- Safe "shutdown" handling and extended typed retrieval methods.

- Wrap RedissonClient for persistent/distributed caching.
- Safe "shutdown" handling and extended typed retrieval methods.

### Locking Mechanism

One of the module's chief advantages is lock-based operations.
• Redisson RLock is used when a Redis-based lock is required, typically for distributed locks.
• ReentrantLock is used for local concurrency in non-Redis caches.

The built-in logic (execute(...) in AbstractLockableCache) handles tryLock with configurable wait time, ensuring any thread that fails to acquire the lock can time out gracefully. This lock-based approach ensures that only one thread updates or computes a given cache entry at a time.
The built-in logic (execute(...) in AbstractLockableCache) handles tryLock with configurable wait time, ensuring any
thread that fails to acquire the lock can time out gracefully. This lock-based approach ensures that only one thread
updates or computes a given cache entry at a time.

## Contributing

Contributions are always welcome. You can:

- Report bugs or suggest features through the issue tracker.
- Create pull requests with enhancements, optimizations, or documentation improvements.
- Expand functionality by adding new cache implementations or multi-tier strategies.
Expand Down
Loading

0 comments on commit b31175a

Please sign in to comment.