Optional dependencies
What are optional dependencies?
There are situations where you create a standalone module and do not know whether a component should be provided or not. This may dependent on the runtime configuration or on whether a component has been provided by another module.
Or, alternatively, a provided component may depend on another component that could potentially not be provided by some other module.
In this case, you can use an OptionalDependency
.
An OptionalDependency
is similar to the Optional
class in Java. It can either have a component provided or not.
Providing an optional dependency
Your module can explicitly provide an optional dependency, and another module can depend on it. Let's say we have some kind of service that needs to be provided based on the runtime configuration.
Here is an example:
public class ServiceModule extends AbstractModule {
@Provides
OptionalDependency<Service> service(Config config) {
boolean shouldBeProvided = config.get(ofBoolean(), "service.isProvided");
if (shouldBeProvided) {
return OptionalDependency.of(new Service());
} else {
return OptionalDependency.empty();
}
}
}
public class ServiceStringModule extends AbstractModule {
@Provides
String serviceString(OptionalDependency<Service> optionalService) {
if (optionalService.isPresent()) {
Service service = optionalService.get();
return service.getString();
} else {
return "none";
}
}
}
If service.isProvided
is set to true
in the configuration, the Service
will be provided and the ServiceStringModule
will provide the String form the Service. Otherwise, the default String will be provided.
Implicit optional dependency
Sometimes, when creating a standalone module you do not know beforehand whether some dependency is provided by another module.
In this case, you can still use OptionalDependency
.
Suppose we have a ServiceModule
which may or may not be installed in the Injector
:
public class ServiceModule extends AbstractModule {
@Provides
Service service() {
return new Service();
}
}
And the ServiceStringModule
, which does not know if ServiceModule
is installed:
public class ServiceStringModule extends AbstractModule {
@Provides
String serviceString(OptionalDependency<Service> optionalService) {
if (optionalService.isPresent()) {
Service service = optionalService.get();
return service.getString();
} else {
return "none";
}
}
}
Although, we have not provided an OptionalDependency<Service>
explicitly, ActiveJ Inject automatically resolves such dependencies.
Thus, if a ServiceModule
is installed and Service
is actually provided, a ServiceStringModule
will receive an OptionalDependency
of the provided Service
. Otherwise, if no ServiceModule
is installed and no Service
is provided, the ServiceStringModule
will receive an
empty OptionalDependency
.
You may wonder why we had to come up with an explicit OptionalDependency
class instead of using the Optional
class from Java.
The reason is that OptionalDependency
is explicitly handled in the Service Graph
and also
when registering JMX components. Another reason is that Optional
was designed to be used mainly as a return type.
On the other hand, it is perfectly fine to use OptionalDependency
as a method parameter.