nbbrd / java-service-util Goto Github PK
View Code? Open in Web Editor NEWJava service utilities
License: European Union Public License 1.2
Java service utilities
License: European Union Public License 1.2
Merge mutability
and singleton
concepts into a higher level of abstraction to avoid invalid use cases such as thread-unsafe singleton.
Allow to use a custom service loader such as NetBeans Lookup instead of JDK ServiceLoader.
Most of the time local immutable instances are called like that: new StuffLoader().get()
.
There is no need to create a loader in these cases. So the following shortcut could be used instead: StuffLoader.load()
.
Having a service ...
@ServiceDefinition(quantifier = Quantifier.MULTIPLE, batch = true)
interface Rule { ... }
..., the following code ...
@ServiceProvider
enum GuidingPrinciples implements Rule { ... }
... would generate
@ServiceProvider
public final class GuidingPrinciplesRuleBatch implements RuleBatch {
@Override
public Stream<Rule> getProviders() {
return Stream.of(GuidingPrinciples.values());
}
}
Java 9+ adds the possility to select a provider before instantiating it. This avoids the instantiation of expensive services that would be filtered out during selection.
In Java 9+, a provider method is a public static
no-arg method called provider
where the return type is the service type.
Since this feature is not available on Java 8, a possible workaround would be to generate a delegate wrapper around this method.
Currently, a sorter method has no parameter.
It is possible to filter a service provider with a system property but it uses a static global variable. That global variable makes it difficult to test and prevents some use cases.
It could be interesting to allow a sorter method to have one parameter to inject those properties.
@ServiceDefinition
interface MyService {
@ServiceFilter
boolean isAvailable(Properties properties);
}
@ServiceProvider
class Impl implements MyService {
@Override
public boolean isAvailable(Properties properties) {
return Objects.equals(properties.getProperty("os.name"), "Windows 10");
}
}
@ServiceProviderInterface
would be a simplified version of @ServiceDefinition
with less options and better defaults.
Current issues:
#singleton
and #mutability
usage is confusing#quantifier
is the main option but is not pushed forward clearly to the userMinimum features:
Additional features:
Check that preprocessing is not used as basic and advanced at the same time.
Preprocessor are functional interfaces. Instances of functional interfaces can be created with lambda expressions, method references or constructor references.
Unfortunatly, annotations do not support this kind of reference. Class references must be used instead and require to write a specific class for each preprocessor.
Unexpected exceptions (aka RuntimeException) can occur during the class initialization, the filtering and the sorting. These exceptions are raised by service providers which should be considered unsafe by default.
The goal is to make the loading safer by catching, logging and recovering from such errors.
Allow to filter providers using a system property.
Examples:
javax.net.ssl.trustStoreType=Windows-ROOT
jdk.tls.disabledAlgorithms=SSLv3, RC4
https.cipherSuites=SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA,SSL_DHE_DSS_WITH_DES_CBC_SHA
The following code ...
@ServiceDefinition(quantifier = Quantifier.MULTIPLE)
public interface Translator {
String translate(String text);
String getName();
@ServiceFilter
default boolean isEnabled() {
return FILTER_ON_PROPERTY.apply(getName());
}
static Predicate<String> FILTER_ON_PROPERTY = getFilter("translators", ',', true, true, false);
}
static Predicate<String> getFilter(String key, char separator, boolean onMatch, boolean onMiss, boolean caseSensitive) {
String property = System.getProperty(key);
if (property == null) return value -> onMiss;
String[] values = property.split(String.valueOf(separator), -1);
return value -> Stream.of(values).anyMatch(caseSensitive ? value::equals : value::equalsIgnoreCase) ^ onMatch;
}
... could be simplified as :
@ServiceDefinition(quantifier = Quantifier.MULTIPLE)
public interface Translator {
String translate(String text);
@ServiceFilterOnProperty(key = "translators", separator = ',', onMatch = true, onMiss = true, caseSensitive = false)
String getName();
}
A @ServiceId
annotation would specify which method returns the identifier of a service provider. This identifier might be used to filter the providers in a loader through system properties.
Example:
@ServiceDefinition(quantifier = Quantifier.MULTIPLE)
public interface Translator {
@ServiceId
String getName();
String translate(String text);
}
Add regex pattern to ServiceId
annotation to allow validation of id and/or document expected format.
Some service providers might be slow to start and therefore would require lazy loading.
Not sure if this is a good idea because the code will be more complex just for a few specific use cases that can be avoided.
Batch loading in @ServiceDefinition
is specified by the properties batch
and batchName
. These properties generates a new class.
Example:
@ServiceDefinition ( batch = true, batchName = "a.b.c.SomeBatch" )
interface SomeService { }
A better approach would be to replace these properties by one that specifies the class to use.
Example:
@ServiceDefinition ( batchType = SomeBatch.class )
interface SomeService { }
interface SomeBatch {
Stream<SomeService > getProviders();
}
Add the possibility to load a bach of providers.
Example:
public interface HelloService {}
@ServiceProvider
public class Impl1 implements HelloService {}
@ServiceProvider
public class Impl2 implements HelloService {}
public class SomeClass {
@ServiceProviderList
public Collection<? extends HelloService> getProviders() { ... }
}
Improve error messages so that problems can be solved with a copy-paste.
Example: replace
Missing module-info.java 'provides' directive for 'demetra.information.InformationExtractor/jdplus.modelling.extractors.ResidualsExtractors.Dynamic'
with
Missing module-info.java directive: 'provides demetra.information.InformationExtractor with jdplus.modelling.extractors.ResidualsExtractors.Dynamic;
And group by service to copy-paste code like:
provides demetra.information.InformationExtractor with
jdplus.modelling.extractors.ResidualsExtractors.Dynamic,
jdplus.modelling.extractors.LikelihoodStatisticsExtractor;
A ServiceSupport
annotation would enforce good pratices when creating service providers.
Example:
@ServiceDefinition
public interface Driver {
String getName();
Closeable openConnection();
}
@ServiceSupport
@lombok.Builder ( toBuilder = true )
public final class QuickDriverSupport implements Driver {
@lombok.Getter
@lombok.Builder.Default
private final String name;
@Override
public Closeable openConnection() { ... }
}
@ServiceProvider
public final class Driver1 implements Driver {
@lombok.experimental.Delegate
private final Driver delegate = QuickDriverSupport.builder().name("X1").build();
}
@ServiceProvider
public final class Driver2 implements Driver {
@lombok.experimental.Delegate
private final Driver delegate = QuickDriverSupport.builder().name("X2").build();
}
Add property to customize batch method name.
The purpose is to allow a class to implement multiple batch interfaces.
This example:
@ServiceDefinition ( batch=true )
public interface ReaderSpi {}
@ServiceDefinition ( batch=true )
public interface WriterSpi {}
@ServiceProvider
public class BatchReaderImpl implements ReaderSpiBatch {
Stream<ReaderSpi> getProviders() { ... }
}
@ServiceProvider
public class BatchWriterImpl implements WriterSpiBatch {
Stream<WriterSpi> getProviders() { ... }
}
Could be replaced with:
@ServiceDefinition ( batch=true, batchMethodName="getReaders" )
public interface ReaderSpi {}
@ServiceDefinition ( batch=true, batchMethodName="getWriters" )
public interface WriterSpi {}
@ServiceProvider ( ReaderSpiBatch.class )
@ServiceProvider ( WriterSpiBatch.class )
public class SomeImpl implements ReaderSpiBatch, WriterSpiBatch {
Stream<ReaderSpi> getReaders() { ... }
Stream<WriterSpi> getWriters() { ... }
}
Another solution would be to customize the method name using the SPI class name:
@ServiceDefinition ( batch=true, autoBatchMethodName=true )
public interface ReaderSpi {}
@ServiceDefinition ( batch=true, autoBatchMethodName=true )
public interface WriterSpi {}
@ServiceProvider ( ReaderSpiBatch.class )
@ServiceProvider ( WriterSpiBatch.class )
public class SomeImpl implements ReaderSpiBatch, WriterSpiBatch {
Stream<ReaderSpi> getReaderSpiProviders() { ... }
Stream<WriterSpi> getWriterSpiProviders() { ... }
}
Missing checks:
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.