Skip to main content

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.

note

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.