A flexible Java ClassLoader that can load classes from 34+ transport protocols with built-in caching support and authentication.
- Local Class Loading: Load classes from local file system directories
- Remote Class Loading: Load classes from HTTP/HTTPS URLs with JAR support
- FTP/FTPS Support: Load classes from FTP and FTPS servers
- Nexus Repository Support: Load classes from Sonatype Nexus repositories (both raw and Maven repositories)
- Maven Artifact Resolution: Automatically extract classes from Maven JARs hosted in Nexus
- Cloud Storage (via jcloudstorage): AWS S3, Azure Blob, Google Cloud Storage, Google Drive, Dropbox, OneDrive
- File Transfer (via jfiletransfer): SFTP, WebDAV, SMB/CIFS, FTP/FTPS
- Messaging (via jmessaging): Kafka, RabbitMQ, Redis
- Containers (via jcontainer): Kubernetes ConfigMaps, Docker, Hazelcast
- Version Control (via jvcs): Git (local and remote)
- Databases: Load classes from JDBC-accessible databases
- Delegation Strategies: Choose parent-first (standard), parent-last (isolation), or custom delegation
- Lifecycle Hooks: Monitor class loading events for tracking, logging, and resource management
- Resource Tracking: Track loaded classes and open resources for cleanup
- Caching: Optional file-system based caching to avoid repeated downloads
- Authentication: Support for HTTP Basic and Bearer token authentication
- Builder Pattern: Fluent API for easy configuration
- Extensible: Add custom class sources by implementing the
ClassSourceinterface - Well Tested: Comprehensive test suite with 392 unit tests (46% code coverage)
- Java 11 or higher
- Maven 3.6 or higher (for building)
<dependency>
<groupId>org.flossware</groupId>
<artifactId>jclassloader</artifactId>
<version>1.1</version>
</dependency>git clone https://github.com/FlossWare/jclassloader.git
cd jclassloader
mvn clean installimport org.flossware.jclassloader.JClassLoader;
JClassLoader loader = JClassLoader.builder()
.addLocalSource("/path/to/classes")
.useCache(false)
.build();
Class<?> myClass = loader.loadClass("com.example.MyClass");
Object instance = myClass.getDeclaredConstructor().newInstance();JClassLoader loader = JClassLoader.builder()
.addRemoteSource("https://example.com/classes/")
.build();
Class<?> myClass = loader.loadClass("com.example.MyClass");import org.flossware.jclassloader.AuthConfig;
// Basic Authentication
AuthConfig basicAuth = AuthConfig.basic("username", "password");
JClassLoader loader = JClassLoader.builder()
.addRemoteSource("https://secure.example.com/classes/", basicAuth)
.build();
// Bearer Token Authentication
AuthConfig bearerAuth = AuthConfig.bearer("your-token-here");
JClassLoader loader2 = JClassLoader.builder()
.addRemoteSource("https://api.example.com/classes/", bearerAuth)
.build();// Anonymous FTP
JClassLoader ftpLoader = JClassLoader.builder()
.addClassSource(new FtpClassSource("ftp://ftp.example.com/classes/"))
.build();
// Authenticated FTP
JClassLoader ftpAuthLoader = JClassLoader.builder()
.addClassSource(new FtpClassSource("ftp://ftp.example.com/classes/", "username", "password"))
.build();
// FTPS (FTP over SSL/TLS)
JClassLoader ftpsLoader = JClassLoader.builder()
.addClassSource(new FtpClassSource("ftps://secure-ftp.example.com/classes/", "username", "password"))
.build();import org.flossware.jclassloader.AuthConfig;
// Public Nexus raw repository
JClassLoader loader = JClassLoader.builder()
.addNexusRawSource("https://nexus.example.com", "raw-classes")
.build();
// Authenticated Nexus raw repository
AuthConfig auth = AuthConfig.basic("username", "password");
JClassLoader authLoader = JClassLoader.builder()
.addNexusRawSource("https://nexus.example.com", "private-raw", auth)
.build();Load classes from JAR files stored as Maven artifacts:
import org.flossware.jclassloader.MavenNexusClassSource;
import org.flossware.jclassloader.MavenArtifact;
// Create a Maven Nexus source with specific artifacts
MavenNexusClassSource nexusSource = MavenNexusClassSource.builder()
.nexusUrl("https://nexus.example.com")
.repository("maven-releases")
.addArtifact("org.apache.commons:commons-lang3:3.12.0")
.addArtifact("com.google.guava:guava:32.1.0-jre")
.build();
JClassLoader loader = JClassLoader.builder()
.addNexusMavenSource(nexusSource)
.build();
// Load a class from one of the configured artifacts
Class<?> stringUtils = loader.loadClass("org.apache.commons.lang3.StringUtils");// Using Basic Authentication
AuthConfig basicAuth = AuthConfig.basic("nexus-user", "nexus-password");
MavenNexusClassSource nexusSource = MavenNexusClassSource.builder()
.nexusUrl("https://private-nexus.example.com")
.repository("private-releases")
.addArtifact("com.company:internal-lib:1.0.0")
.auth(basicAuth)
.build();
// Using Bearer Token (for Nexus 3 with token authentication)
AuthConfig tokenAuth = AuthConfig.bearer("NexusToken12345");
MavenNexusClassSource tokenSource = MavenNexusClassSource.builder()
.nexusUrl("https://nexus.example.com")
.repository("releases")
.addArtifact("com.company:api-client:2.0.0")
.auth(tokenAuth)
.build();import org.flossware.jclassloader.MavenArtifact;
// Parse Maven coordinates
MavenArtifact artifact = MavenArtifact.parse("org.example:my-lib:1.0.0");
System.out.println(artifact.getGroupId()); // org.example
System.out.println(artifact.getArtifactId()); // my-lib
System.out.println(artifact.getVersion()); // 1.0.0
// With classifier and packaging
MavenArtifact sources = MavenArtifact.parse("org.example:my-lib:1.0.0:sources:jar");
// Convert to repository path
String path = artifact.toPath(); // org/example/my-lib/1.0.0/my-lib-1.0.0.jarimport org.flossware.jclassloader.cache.FileSystemCache;
FileSystemCache cache = new FileSystemCache("/tmp/jclassloader-cache");
JClassLoader loader = JClassLoader.builder()
.addRemoteSource("https://example.com/classes/")
.cache(cache)
.useCache(true)
.build();
// First load: downloads from remote and caches
Class<?> myClass = loader.loadClass("com.example.MyClass");
// Subsequent loads: uses cached version
Class<?> myCachedClass = loader.loadClass("com.example.MyClass");Classes are searched in the order sources are added:
JClassLoader loader = JClassLoader.builder()
.addLocalSource("/opt/app/classes") // Searched first
.addRemoteSource("https://cdn.example.com/") // Searched second
.addClassSource(new FtpClassSource("ftp://backup.example.com/classes/")) // Searched third
.cache(new FileSystemCache("/tmp/cache"))
.build();Load classes from cloud storage providers using the jcloudstorage library:
<!-- Add jcloudstorage dependency -->
<dependency>
<groupId>org.flossware</groupId>
<artifactId>jcloudstorage</artifactId>
<version>1.0</version>
</dependency>
<!-- Add provider SDK (only what you need) -->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
<version>2.44.12</version>
</dependency>import org.flossware.cloud.storage.CloudStorageClient;
import org.flossware.cloud.storage.S3CloudStorageClient;
import org.flossware.jclassloader.CloudStorageClassSource;
// Create cloud storage client
CloudStorageClient s3 = S3CloudStorageClient.builder()
.bucket("my-classes-bucket")
.region("us-east-1")
.prefix("production/classes/")
.build();
// Use with JClassLoader
JClassLoader loader = JClassLoader.builder()
.addCloudStorage(s3)
.build();
// Or wrap manually
JClassLoader loader2 = JClassLoader.builder()
.addClassSource(new CloudStorageClassSource(s3))
.build();Supported cloud providers (via jcloudstorage):
- AWS S3
- Azure Blob Storage
- Google Cloud Storage
- Google Drive
- Dropbox
- OneDrive
See jcloudstorage documentation for provider-specific configuration.
Load classes via file transfer protocols using jfiletransfer:
<dependency>
<groupId>org.flossware</groupId>
<artifactId>jfiletransfer</artifactId>
<version>1.0</version>
</dependency>import org.flossware.filetransfer.FileTransferClient;
import org.flossware.filetransfer.SftpFileTransferClient;
import org.flossware.jclassloader.FileTransferClassSource;
// SFTP example
FileTransferClient sftp = SftpFileTransferClient.builder()
.host("sftp.example.com")
.username("deploy")
.password("secret")
.basePath("/opt/classes")
.build();
JClassLoader loader = JClassLoader.builder()
.addClassSource(new FileTransferClassSource(sftp))
.build();Supported protocols: SFTP, WebDAV, SMB/CIFS, FTP/FTPS. See jfiletransfer docs.
Load classes from messaging systems using jmessaging:
<dependency>
<groupId>org.flossware</groupId>
<artifactId>jmessaging</artifactId>
<version>1.0</version>
</dependency>import org.flossware.messaging.MessageClient;
import org.flossware.messaging.KafkaMessageClient;
import org.flossware.jclassloader.MessageClientClassSource;
// Kafka example
MessageClient kafka = KafkaMessageClient.builder()
.bootstrapServers("kafka:9092")
.topic("class-definitions")
.build();
JClassLoader loader = JClassLoader.builder()
.addClassSource(new MessageClientClassSource(kafka))
.build();Supported systems: Kafka, RabbitMQ, Redis. See jmessaging docs.
Load classes from containers using jcontainer:
<dependency>
<groupId>org.flossware</groupId>
<artifactId>jcontainer</artifactId>
<version>1.0</version>
</dependency>import org.flossware.container.ContainerClient;
import org.flossware.container.KubernetesContainerClient;
import org.flossware.jclassloader.ContainerClientClassSource;
// Kubernetes ConfigMap example
ContainerClient k8s = KubernetesContainerClient.builder()
.namespace("production")
.build();
JClassLoader loader = JClassLoader.builder()
.addClassSource(new ContainerClientClassSource(k8s, "app-classes"))
.build();Supported systems: Kubernetes ConfigMaps, Docker, Hazelcast. See jcontainer docs.
Load classes from version control using jvcs:
<dependency>
<groupId>org.flossware</groupId>
<artifactId>jvcs</artifactId>
<version>1.0</version>
</dependency>import org.flossware.vcs.VcsClient;
import org.flossware.vcs.GitVcsClient;
import org.flossware.jclassloader.VcsClientClassSource;
// Git repository example
VcsClient git = GitVcsClient.builder()
.repositoryPath("/opt/app-repo")
.branch("release/v1.0")
.basePath("build/classes")
.build();
JClassLoader loader = JClassLoader.builder()
.addClassSource(new VcsClientClassSource(git))
.build();Supported systems: Git (local and remote). See jvcs docs.
ClassLoader parentLoader = Thread.currentThread().getContextClassLoader();
JClassLoader loader = JClassLoader.builder()
.parent(parentLoader)
.addLocalSource("/path/to/classes")
.build();JClassLoader now supports configurable delegation strategies to control when classes are loaded from parent vs. configured sources.
// Delegates to parent first, then checks sources
JClassLoader loader = JClassLoader.builder()
.addLocalSource("/path/to/classes")
.parentFirst() // This is the default
.build();Useful for plugin systems, application containers, and isolation scenarios where you want to load application classes before parent classes.
// Checks sources first, then falls back to parent
// System classes (java.*, javax.*) always from parent
JClassLoader loader = JClassLoader.builder()
.addLocalSource("/plugins/my-plugin")
.parentLast("com.myapp.api.", "java.", "javax.") // API classes from parent
.build();Fine-grained control using a predicate:
// Custom logic to decide parent-first vs parent-last per class
JClassLoader loader = JClassLoader.builder()
.addLocalSource("/path/to/classes")
.customDelegation(className ->
className.startsWith("java.") || // System classes: parent-first
className.startsWith("com.platform.api.") // Platform API: parent-first
)
.build();Monitor class loading events for tracking, debugging, and resource management.
// Log all class loading events
JClassLoader loader = JClassLoader.builder()
.addLocalSource("/path/to/classes")
.addLoggingListener() // Logs to System.out
.build();
// Verbose logging (includes cache hits)
JClassLoader verboseLoader = JClassLoader.builder()
.addLocalSource("/path/to/classes")
.addLoggingListener(true) // verbose = true
.build();Track loaded classes and resources for cleanup (useful for hot-reload, plugin unloading, etc.):
import org.flossware.jclassloader.lifecycle.ResourceTrackingListener;
ResourceTrackingListener tracker = new ResourceTrackingListener();
JClassLoader loader = JClassLoader.builder()
.addLocalSource("/plugins/my-plugin")
.addListener(tracker)
.build();
// Load classes...
loader.loadClass("com.example.Plugin");
// Get statistics
System.out.println("Classes loaded: " + tracker.getTotalClassesLoaded());
System.out.println("Bytes loaded: " + tracker.getTotalBytesLoaded());
System.out.println("Cache hits: " + tracker.getCacheHits());
// Cleanup when done (e.g., unloading a plugin)
tracker.closeAllResources();Implement your own listener for custom monitoring:
import org.flossware.jclassloader.lifecycle.ClassLoaderLifecycleListener;
import org.flossware.jclassloader.lifecycle.ClassLoadEvent;
ClassLoaderLifecycleListener myListener = new ClassLoaderLifecycleListener() {
@Override
public void onClassLoaded(ClassLoadEvent event) {
// Custom logic: security checks, metrics, etc.
System.out.println("Loaded: " + event.getClassName() +
" from " + event.getSource().getDescription() +
" in " + event.getLoadTimeMillis() + "ms");
}
@Override
public void onClassLoadFailed(String className, Throwable error) {
// Handle failures
System.err.println("Failed to load: " + className);
}
};
JClassLoader loader = JClassLoader.builder()
.addLocalSource("/path/to/classes")
.addListener(myListener)
.build();Class Loading:
JClassLoader: Main classloader implementation extendingjava.lang.ClassLoaderClassSource: Interface for class loading sources (20+ implementations)LocalClassSource: Loads classes from local file systemRemoteClassSource: Loads classes from HTTP/HTTPS URLsFtpClassSource: Loads classes from FTP/FTPS serversNexusClassSource: Loads classes from Nexus raw or Maven repositoriesMavenNexusClassSource: Specialized source for loading classes from Maven artifacts in NexusMavenArtifact: Represents Maven coordinates and handles path resolution
Delegation & Control (NEW in 1.0):
DelegationStrategy: Interface for delegation strategiesParentFirstDelegation: Standard Java delegation (default)ParentLastDelegation: Sources-first delegation for isolationCustomDelegation: Predicate-based custom delegation
Lifecycle & Monitoring (NEW in 1.0):
ClassLoaderLifecycleListener: Interface for lifecycle eventsClassLoadEvent: Event with class load details (source, time, size)ResourceTrackingListener: Tracks classes and resources for cleanupLoggingListener: Debug logging for class loading
Caching & Auth:
ClassCache: Interface for caching implementationsFileSystemCache: File-based cache implementationAuthConfig: Configuration for authentication
- When
loadClass()is called, the configured delegation strategy determines the order:- Parent-first (default): Checks parent → cache → sources
- Parent-last: Checks cache → sources → parent (except system classes)
- Custom: User-defined logic per class
- If using cache and class is cached, returns cached bytecode
- Otherwise, iterates through configured class sources in order
- The first source that can load the class provides the bytecode
- Lifecycle listeners are notified of the class load event
- The class is defined and optionally cached for future use
Important: Loading classes from remote sources can be a security risk. Only load classes from trusted sources.
- Always use HTTPS instead of HTTP when possible
- Validate the source of remote classes
- Consider implementing checksum verification
- Use authentication for protected resources
- Be aware that cached classes persist between runs
Run the test suite:
mvn testJClassLoader has 463 comprehensive unit tests achieving 46% instruction coverage across the codebase. While we strive for high test coverage, 100% coverage is not the goal for this project. Here's why:
What IS tested (well-covered):
- ✅ Core functionality (53% coverage): Class loading, delegation strategies, authentication
- ✅ Caching layer (83% coverage): FileSystemCache, RedisClassSource
- ✅ Lifecycle system (82% coverage): Event listeners, resource tracking
- ✅ Delegation strategies (85% coverage): Parent-first, parent-last, custom
- ✅ Protocol handling (100% coverage): Protocol registry and handlers
- ✅ Critical paths: All main use cases and builder patterns extensively tested
What is NOT fully tested (and why):
- ❌ Example code (0% coverage in
org.flossware.jclassloader.example): These are demo applications, not production code. Testing them would verify example syntax, not library functionality. - ❌ Deep integration scenarios (66% coverage in messaging): Full integration tests with real Kafka, RabbitMQ, Redis, etc. would require running external infrastructure. We use mocks for unit tests and rely on real-world usage for integration validation.
- ❌ Error recovery edge cases: Some network timeout paths, exotic authentication failures, and rare filesystem conditions are difficult to trigger reliably in unit tests.
- ❌ External system quirks: Vendor-specific behaviors (Nexus API variations, messaging broker retry logic) are validated through manual testing and production usage.
Testing Philosophy:
- Unit tests verify logic, not infrastructure: We mock external services (HTTP clients, FTP connections, database connections) to test our code, not third-party libraries.
- Builder pattern coverage: Every builder is tested for null validation, required fields, and correct object construction.
- Value object contracts: All immutable objects (AuthConfig, MavenArtifact) have equals/hashCode/toString tests.
- Critical security paths: Authentication, SSL/TLS validation, path traversal protection are thoroughly tested.
Coverage by package:
org.flossware.jclassloader (core) 53% ✅ Primary use cases
org.flossware.jclassloader.cache 83% ✅ High reliability needed
org.flossware.jclassloader.lifecycle 82% ✅ Critical for monitoring
org.flossware.jclassloader.delegation 85% ✅ Core isolation feature
org.flossware.jclassloader.protocol 100% ✅ Complete coverage
org.flossware.jclassloader.messaging 66% ⚠️ Kafka/RabbitMQ mocked
org.flossware.jclassloader.example 0% ❌ Demo code, not tested
Running coverage reports:
mvn clean test jacoco:report
# View report at: target/site/jacoco/index.htmlThe 46% coverage represents high-quality, focused testing of the library's core functionality. Additional coverage would primarily test external library behaviors or demo code, providing diminishing returns for the effort invested.
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the GNU General Public License v3.0 - see the LICENSE file for details.
- Scot P. Floess
Use parent-last delegation to isolate plugins from the host application:
JClassLoader pluginLoader = JClassLoader.builder()
.addLocalSource("/plugins/my-plugin")
.parentLast("com.myapp.api.") // Only API from host
.trackResources() // For cleanup when unloading
.build();Run multiple applications in one JVM with isolation:
JClassLoader app1Loader = JClassLoader.builder()
.addLocalSource("/apps/app1.jar")
.addMavenCentral("commons-lang3:3.12.0")
.parentLast("org.platform.api.")
.addListener(resourceTracker)
.build();Isolate tests and cleanup between runs:
ResourceTrackingListener tracker = new ResourceTrackingListener();
JClassLoader testLoader = JClassLoader.builder()
.addLocalSource("/test-classes")
.addListener(tracker)
.build();
// Run tests...
tracker.closeAllResources(); // CleanupLoad tenant-specific code with isolation:
JClassLoader tenantLoader = JClassLoader.builder()
.addRemoteSource("https://tenant1.example.com/code/")
.parentLast()
.build();- ✅ 30+ ClassSource implementations (HTTP, FTP, SFTP, WebDAV, Maven, databases, Kafka, RabbitMQ, Redis, HDFS, Kubernetes, Docker, Git, MinIO, Hazelcast, etc.)
- ✅ Cloud storage support via jcloudstorage library (S3, Azure Blob, GCS, Google Drive, Dropbox, OneDrive)
- ✅ JAR file loading from remote sources (HTTP/HTTPS)
- ✅ Bytecode verification and checksum validation (SHA-256)
- ✅ Retry logic with exponential backoff and jitter
- ✅ Delegation strategies (parent-first, parent-last, custom)
- ✅ Lifecycle hooks for monitoring and resource tracking
- ✅ File system caching
- ✅ Authentication support (Basic, Bearer)
- ✅ Comprehensive test suite (392 tests, 46% coverage)
- More cache implementations (in-memory)
- Performance metrics dashboard
- Class version conflict detection
- Hot reload / class reloading support