Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 14, 2025

AsyncRepository.wrap() created anonymous implementations, preventing repository-specific async optimizations and forcing code duplication across variations.

Changes

New concrete wrapper classes:

  • AsyncRepositoryWrapper<ID, E> - Base wrapper delegating to synchronous repositories
  • AsyncQueryableRepositoryWrapper<ID, E> - Extends base with query support

Refactored factory methods:

  • AsyncRepository.wrap() now returns AsyncRepositoryWrapper instances
  • AsyncQueryableRepository.wrap() now returns AsyncQueryableRepositoryWrapper instances

API additions:

  • getRepository() and getExecutor() accessors on wrappers enable composition
  • Protected fields allow subclass customization

Extensibility enabled

Repository implementations can now provide optimized async versions or add middleware:

// Before: Not possible
// After: Extend wrapper to add caching
public class CachedAsyncRepository<ID, E extends Identifiable<ID>> 
        extends AsyncRepositoryWrapper<ID, E> {
    
    private final Map<ID, E> cache = new ConcurrentHashMap<>();
    
    @Override
    public CompletableFuture<Optional<E>> findById(ID id) {
        E cached = cache.get(id);
        if (cached != null) {
            return CompletableFuture.completedFuture(Optional.of(cached));
        }
        return super.findById(id).thenApply(opt -> {
            opt.ifPresent(e -> cache.put(id, e));
            return opt;
        });
    }
}

Backward compatibility

All existing calls to AsyncRepository.wrap() and Database.getAsyncRepository() work unchanged. The static methods now return concrete classes instead of anonymous implementations.

Testing

  • Unit tests added for both wrapper classes using Mockito
  • All existing integration tests pass without modification

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • hub.spigotmc.org
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED -XX:MaxMetaspaceSize=384m -XX:&#43;HeapDumpOnOutOfMemoryError -Xms256m -Xmx512m -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant -cp /home/REDACTED/.gradle/wrapper/dists/gradle-8.13-bin/5xuhj0ry160q40clulazy9h7d/gradle-8.13/lib/gradle-daemon-main-8.13.jar -javaagent:/home/REDACTED/.gradle/wrapper/dists/gradle-8.13-bin/5xuhj0ry160q40clulazy9h7d/gradle-8.13/lib/agents/gradle-instrumentation-agent-8.13.jar org.gradle.launcher.daemon.bootstrap.GradleDaemon 8.13 (dns block)
  • jitpack.io
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED -XX:MaxMetaspaceSize=384m -XX:&#43;HeapDumpOnOutOfMemoryError -Xms256m -Xmx512m -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant -cp /home/REDACTED/.gradle/wrapper/dists/gradle-8.13-bin/5xuhj0ry160q40clulazy9h7d/gradle-8.13/lib/gradle-daemon-main-8.13.jar -javaagent:/home/REDACTED/.gradle/wrapper/dists/gradle-8.13-bin/5xuhj0ry160q40clulazy9h7d/gradle-8.13/lib/agents/gradle-instrumentation-agent-8.13.jar org.gradle.launcher.daemon.bootstrap.GradleDaemon 8.13 (dns block)
  • s01.oss.sonatype.org
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED -XX:MaxMetaspaceSize=384m -XX:&#43;HeapDumpOnOutOfMemoryError -Xms256m -Xmx512m -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant -cp /home/REDACTED/.gradle/wrapper/dists/gradle-8.13-bin/5xuhj0ry160q40clulazy9h7d/gradle-8.13/lib/gradle-daemon-main-8.13.jar -javaagent:/home/REDACTED/.gradle/wrapper/dists/gradle-8.13-bin/5xuhj0ry160q40clulazy9h7d/gradle-8.13/lib/agents/gradle-instrumentation-agent-8.13.jar org.gradle.launcher.daemon.bootstrap.GradleDaemon 8.13 (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

Right now. All the Async Repository are created using the AsyncRepository#wrap method. This causes to break all the abstraction, implementation of the original repository and forces me to create a async implementation for all repository variations. Suggest implementations improvements & new ideas to fix this issue and to make the code more future proof. I would also like to avoid class repeating themselves (unless forces too). The goal is to make the code as straightforward for the final developer using this library.


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 3 commits November 14, 2025 18:40
- Created AsyncRepositoryWrapper and AsyncQueryableRepositoryWrapper classes
- Updated AsyncRepository.wrap() and AsyncQueryableRepository.wrap() to use concrete wrapper classes
- Added Mockito dependency for testing
- Added unit tests for both wrapper classes
- All tests passing, backward compatibility maintained

Co-authored-by: Robotv2 <81443374+Robotv2@users.noreply.github.com>
Co-authored-by: Robotv2 <81443374+Robotv2@users.noreply.github.com>
Copilot AI changed the title [WIP] Refactor async repository implementation for better abstraction Replace anonymous async repository implementations with concrete wrapper classes Nov 14, 2025
Copilot AI requested a review from Robotv2 November 14, 2025 18:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants