WaveletProvider Service Provider Interface (SPI)
Overview
The WaveletProvider interface enables automatic discovery and registration of wavelets using Java's ServiceLoader mechanism. This provides a plugin architecture where third-party wavelets can be added without modifying the core VectorWave library.
Interface Definition
package com.morphiqlabs.wavelet.api;
public interface WaveletProvider {
/**
* Returns the wavelets provided by this implementation.
*
* @return list of wavelets (must not be null or empty)
*/
List<Wavelet> getWavelets();
}
Implementation Guide
Step 1: Create Your Wavelet Implementation
package com.example.wavelets;
import com.morphiqlabs.wavelet.api.OrthogonalWavelet;
public record MyCustomWavelet() implements OrthogonalWavelet {
@Override
public String name() {
return "custom1";
}
@Override
public String description() {
return "My custom orthogonal wavelet";
}
@Override
public double[] lowPassDecomposition() {
return new double[]{0.5, 0.5, 0.5, 0.5}; // Example coefficients
}
@Override
public double[] highPassDecomposition() {
// Generate using QMF or provide directly
return OrthogonalWavelet.computeHighPassFromLowPass(lowPassDecomposition());
}
@Override
public int vanishingMoments() {
return 2;
}
}
Step 2: Create a Provider Implementation
package com.example.wavelets;
import com.morphiqlabs.wavelet.api.Wavelet;
import com.morphiqlabs.wavelet.api.WaveletProvider;
import java.util.List;
public class CustomWaveletProvider implements WaveletProvider {
@Override
public List<Wavelet> getWavelets() {
return List.of(
new MyCustomWavelet(),
new AnotherCustomWavelet()
);
}
}
Step 3: Register the Provider
Create a file META-INF/services/com.morphiqlabs.wavelet.api.WaveletProvider in your resources directory:
com.example.wavelets.CustomWaveletProvider
Step 4: Package and Use
Once packaged in a JAR and added to the classpath, your wavelets will be automatically discovered:
// Your wavelets are now available
Wavelet custom = WaveletRegistry.getWavelet("custom1");
Best Practices
1. Naming Conventions
- Use lowercase names for consistency
- Avoid conflicts with existing wavelets
- Consider prefixing with organization/project name
@Override
public String name() {
return "acme-fast1"; // Prefixed to avoid conflicts
}
2. Lazy Initialization
For wavelets with expensive initialization:
public class LazyWaveletProvider implements WaveletProvider {
private volatile List<Wavelet> wavelets;
@Override
public List<Wavelet> getWavelets() {
if (wavelets == null) {
synchronized (this) {
if (wavelets == null) {
wavelets = createWavelets();
}
}
}
return wavelets;
}
private List<Wavelet> createWavelets() {
// Expensive initialization here
return List.of(...);
}
}
3. Error Handling
The WaveletRegistry gracefully handles provider errors:
public class RobustProvider implements WaveletProvider {
@Override
public List<Wavelet> getWavelets() {
try {
return loadWavelets();
} catch (Exception e) {
// Log error
System.err.println("Failed to load custom wavelets: " + e);
// Return empty list or partial results
return List.of();
}
}
}
4. Conditional Loading
Load wavelets based on configuration or environment:
public class ConditionalProvider implements WaveletProvider {
@Override
public List<Wavelet> getWavelets() {
List<Wavelet> wavelets = new ArrayList<>();
if (System.getProperty("enable.experimental.wavelets") != null) {
wavelets.add(new ExperimentalWavelet());
}
wavelets.add(new StandardCustomWavelet());
return wavelets;
}
}
Testing Your Provider
@Test
void testProviderDiscovery() {
// Verify provider is discovered
ServiceLoader<WaveletProvider> loader = ServiceLoader.load(WaveletProvider.class);
boolean found = false;
for (WaveletProvider provider : loader) {
if (provider instanceof CustomWaveletProvider) {
found = true;
break;
}
}
assertTrue(found);
}
@Test
void testWaveletRegistration() {
// Verify wavelets are registered
assertTrue(WaveletRegistry.hasWavelet("custom1"));
Wavelet wavelet = WaveletRegistry.getWavelet("custom1");
assertNotNull(wavelet);
assertEquals("custom1", wavelet.name());
}
Module System Support
For Java 9+ modules, declare the service in module-info.java:
module com.example.wavelets {
requires com.morphiqlabs.vectorwave;
provides com.morphiqlabs.wavelet.api.WaveletProvider
with com.example.wavelets.CustomWaveletProvider;
}
Troubleshooting
Wavelets Not Found
- Verify
META-INF/servicesfile is in the correct location - Check the provider class name is fully qualified
- Ensure the JAR is on the classpath
- Call
WaveletRegistry.reload()if loading dynamically
ClassNotFoundException
- Ensure all dependencies are available
- Check for typos in the service file
- Verify the provider has a public no-args constructor
Performance Issues
- Avoid heavy computation in
getWavelets() - Cache wavelet instances if they're expensive to create
- Consider lazy initialization for large wavelet sets
Built-in Providers
VectorWave includes several built-in providers as examples:
OrthogonalWaveletProvider: Haar, Daubechies, Symlets, CoifletsBiorthogonalWaveletProvider: Biorthogonal spline waveletsContinuousWaveletProvider: Morlet, Gaussian derivativesFinancialWaveletProvider: Paul, DOG, Shannon wavelets
Study these implementations for best practices and patterns.