Skip to main content

Optional dependencies

What are optional dependencies?

There are situations when you create a standalone module and do not know whether a component should be provided or not. This may dependent on runtime configuration or whether some component was 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 cases you can use an OptionalDependency.

An OptionalDependency is similar to Java's Optional class. It can either have a provided component or not.

Providing an optional dependency

Your module can provide an optional dependency explicitly and another module can depend on it. Let's say we have some service that should be provided based on 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 a configuration has service.isProvided value set to true, then a Service will be provided and a ServiceStringModule will provide a String out of the Service. Otherwise, a default String will be provided.

Implicit optional dependency

Sometimes, when creating a standalone module you do not know in advance whether some dependency is provided by another module. In this case you can still depend on OptionalDependency.

Let's say we have a ServiceModule that may or may not be installed to the Injector:

public class ServiceModule extends AbstractModule {
    @Provides    Service service() {        return new Service();    }}

And a ServiceStringModule that does not know whether a 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 did not provide an OptionalDependency<Service> explicitly, ActiveJ Inject automatically resolves such dependencies. So, if a ServiceModule is installed and Service is actually provided, a ServiceStringModule will receive an OptionalDependency of a provided Service. Otherwise, if ServiceModule is not installed and Service is not provided, a ServiceStringModule will receive an empty OptionalDependency.

note

You may wonder why we had to come up with an explicit OptionalDependency class and not use Java's Optional instead. The reason is that OptionalDependency is explicitly handled in Service Graph as well as during registration of JMX components. Another reason is that Optional was designed to be used as a return type primarily. On the other hand, it is perfectly fine to use OptionalDependency as method parameter.