Feather modules
Inversion of Control and Dependency Injection
To better understand the following sections, let's look at the Inversion of Control (IoC) principle and the Dependency Injection (DI) pattern.
IoC is a principle in software engineering that transfers the control of objects to a container or a framework. In contrast with traditional programming, in which custom code makes calls to a library, IoC enables a framework to control a program flow and make calls to custom code. To enable this, frameworks use abstractions with additional behavior built-in.
The advantages of this architecture are as follows:
- decoupling task execution from its implementation
- making it easier to switch between different implementations
- greater program modularity
- easier program testing by isolating a component or mocking its dependencies
Dependency injection is a pattern that implements the IoC principle. In this pattern, the control is inverted to set up an object's dependencies. Using DI, you move the creation and binding of dependent objects outside of the class that depends on them. Connecting objects with other objects or injecting objects into other objects is done by an assembler (injector) rather than by objects themselves.
The DI pattern usually involves three types of classes:
- Client depends on the service class.
- Service provides a service to the client class.
- Injector injects the service class object into the client class.

Here's an example of how to create an object dependency in traditional programming:
public class Client {
private Service service;
public Client() {
service = new ServiceImpl();
}
}
In the example above, you need to instantiate an implementation of the Service interface within the Client class itself.
By using DI, you can rewrite the example without specifying the implementation of the Service you want:
public class Client {
private Service service;
@Inject
public Client(Service service) {
this.service = service;
}
}
With the DI pattern, the Client class is no longer responsible for instantiating the objects it requires. Those responsibilities are passed to the outer framework or container.
tip
Both Inversion of Control and Dependency Injection are simple concepts, but they have deep implications for structuring your code. To get deeper into the details, read the following articles by Martin Fowler:
ODF 2 and Feather container
A container is a common characteristic of frameworks that implement IoC. It manages object creation and its lifetime and injects dependencies into the class. In the ODF 2 framework, we use Feather—an ultra-lightweight DI library for Java responsible for creating and injecting objects and basically representing a container.
The ODF 2 framework creates a new Feather container for each Bot Task instance. As a Bot Task developer, you don't need any extra configuration. When you work with a Bot Task, it's already a part of the Feather container.
@BotTask
public class SimpleBotTask implements GenericTask {
private CurrentTransaction transaction;
@Inject
public SimpleBotTask(CurrentTransaction transaction) {
this.transaction = transaction;
}
}
In the example above, the CurrentTransaction object is injected through the class constructor. To make the framework aware that CurrentTransaction is expected to be injected by the framework, the @javax.inject.Inject annotation is used. The ODF 2 framework creates a proper instance of the CurrentTransaction class to be used during the SimpleBotTask class construction when it comes to runtime.
There are plenty of out-of-the-box objects to be injected into a Bot Task, and we will get to them later, but for now, let's understand where these objects come from.
Feather module
A module in terms of the ODF 2 framework is a configuration that defines which objects are available within a container.
Let's take a look at a typical module:
public class TimeModule implements OdfModule {
@Provides
@Singleton
public OdfTime odfTime() {
return new ProductionTime();
}
}
As you can see, the module is a simple Java class that implements the com.workfusion.odf2.core.cdi.OdfModule interface. The interface is a marker interface declaring that the class contains one or more methods annotated with org.codejargon.feather.Provides and may be processed by the framework. The @Provides annotation tells the framework that a method will return an object that can be registered within the Feather container.
javax.inject.Singleton is another useful annotation. When an object is a singleton, only one shared instance of the object is managed, and all requests for that object result in that one specific instance being returned by the Feather container.
ODF 2 comes with multiple predefined modules already available in every Bot Task. You can also define your own modules and provide required objects to be processed within the Feather container.

To put it all together, let's create a custom module and register Service inside it to be further used in your Bot Task:
public class CustomModule implements OdfModule {
@Provides
@Singleton
public Service service() {
return new ServiceImpl();
}
}
Bind the
ServiceImplclass to the interfaceService. This is particularly useful as the module clients can obtain an object based on the interface rather than the implementation.Create a Bot Task and use
CustomModuleinside it.@BotTask @Requires(CustomModule.class) public class SimpleBotTask implements GenericTask { private Service service; @Inject public SimpleBotTask(Service service) { this.service = service; } @Override public void run() { service.importantMethod(); } }Use the
com.workfusion.odf2.core.cdi.Requiresannotation in yourSimpleBotTask. This annotation bindsCustomModulewith theFeathercontainer and lets all the@Providesobjects from the module be available within the container and therefore within your Bot Task.Inject the required
Serviceby the interface.
You can also create multiple modules. Mind that the @Requires annotation perfectly works with the module class as well. You can even reuse objects from one module inside another.
public class FooModule implements OdfModule {
@Provides
public Foo foo() {
return new FooImpl();
}
}
@Requires(FooModule.class)
public class BarModule implements OdfModule {
@Provides
public Bar bar(Foo foo) {
return new BarImpl(foo);
}
}
note
After you declare the Foo object in FooModule, you can access it using the method parameter inside BarModule. In the same way, you can refer to any object available within the Feather container.
ODF2 built-in modules
The section describes modules and their related objects that are available out-of-the-box in the ODF 2 framework.
Database module
com.workfusion.odf2.core.orm.DatabaseModule provides objects related to the database, ORM entities, and repositories.
| Java class | Description |
|---|---|
| javax.sql.DataSource | Data Source associated with the database properties from the current WebHarvest context |
| com.j256.ormlite.support.ConnectionSource | OrmLite connection source that encapsulates the current Data Source; the object is required for creating the OrmLite repository |
| com.workfusion.odf2.core.orm.repository.TransactionRepository | OrmLite repository responsible for Transaction entities |
| com.workfusion.odf2.core.orm.repository.ErrorRepository | OrmLite repository responsible for ErrorEntity entities |
| com.workfusion.odf2.core.orm.repository.MonitorRepository | OrmLite repository responsible for MonitorEntity entities |
| com.workfusion.odf2.core.orm.repository.JoinRepository | OrmLite repository responsible for JoinEntity entities |
| com.workfusion.odf2.core.orm.repository.TransactionStageRepository | OrmLite repository responsible for Stage entities |
| com.workfusion.odf2.core.orm.repository.TransactionStageLogRepository | OrmLite repository responsible for TransactionStageLog entities |
| com.workfusion.odf2.core.orm.repository.MonitorConfigurationRepository | OrmLite repository responsible for MonitorConfigurationEntity entities |
| com.workfusion.odf2.core.orm.repository.ConfigRepository | OrmLite repository responsible for ConfigEntity entities; usually accessed through the com.workfusion.odf2.core.settings.Configuration object |
Services module
com.workfusion.odf2.core.service.ServicesModule provides common ODF 2 services.
| Java class | Description |
|---|---|
| com.workfusion.odf2.core.service.StageLoggingService | logger service for transaction stage events like start and stop |
Settings module
com.workfusion.odf2.core.settings.SettingsModule provides common ODF 2 settings objects.
| Java class | Description |
|---|---|
| com.workfusion.odf2.core.settings.UseCaseSettings | object that handles Use Case settings associated with the current project, for example, code, name, version |
| com.workfusion.odf2.core.settings.Configuration | key-value storage that serializes data using com.workfusion.odf2.core.orm.repository.ConfigRepository and ConfigEntity |
Transaction module
com.workfusion.odf2.core.transaction.TransactionModule is responsible for handling the Transaction entities.
| Java class | Description |
|---|---|
| com.workfusion.odf2.core.transaction.CurrentTransaction | object that holds the current transaction state meaning the transaction of this specific step |
| com.workfusion.odf2.core.transaction.TransactionOperation | object that provides core methods for handling the Transaction entity |
| com.workfusion.odf2.core.transaction.TransactionBuilder | object that provides core methods for building a new Transaction entity |
WebHarvest Integration module
com.workfusion.odf2.core.webharvest.WebHarvestIntegrationModule provides access to objects related to the WebHarvest context.
| Java class | Description |
|---|---|
| groovy.lang.Binding | Groovy binding from the WebHarvest context associated with the current step |
| com.workfusion.odf2.core.webharvest.BindingReader | utility class that eases reading values from the Groovy binding |
| com.workfusion.odf2.core.webharvest.TaskInput | input data associated with the current step |
| com.workfusion.odf2.core.webharvest.TaskOutput | output data associated with the current step |
| @BpRunId java.util.UUID | current Business Process's run ID; can be injected using the @BpRunId qualifier |
| com.workfusion.utils.security.DatabaseProperties | database properties extracted from the current WebHarvest context |
| org.webharvest.runtime.Scraper | WebHarvest scraper associated with the current step |
| com.workfusion.odf2.core.task.rpa.RpaFactory | factory object responsible for creating RPA runners |
| com.workfusion.odf2.core.webharvest.service.vault.SecretsVaultService | Secrets Vault service responsible for operations regarding to Secrets Vault |
| com.workfusion.odf2.core.webharvest.service.s3.S3ConnectionProperties | S3 connection properties extracted from the current WebHarvest context |
| org.slf4j.Logger | instance of a common Logger associated with the current step |
| com.freedomoss.crowdcontrol.webharvest.web.WebServiceConnectionProperties | connection properties related to the Control Tower instance which the current step is bounded to |
| com.workfusion.utils.security.Credentials | user credentials extracted from the current WebHarvest context |
WebHarvest Services module
com.workfusion.odf2.core.webharvest.service.WebHarvestServicesModule provides access to WebHarvest related services.
| Java class | Description |
|---|---|
| com.workfusion.odf2.core.webharvest.service.pool.PoolObjectFactory | factory class that provides Java API for the WebHarvest's pool plugin |
| com.workfusion.odf2.core.webharvest.service.CacheService | service class that provides Java API for the WebHarvest's cache plugin |
| com.workfusion.odf2.core.webharvest.service.ControlTowerTaskService | service class responsible for starting a Business Process inside Control Tower; the class behaves identically to the WebHarvest's task-start plugin |
| com.amazonaws.services.s3.AmazonS3 | default S3 client associated with the S3 instance specified in the WebHarvest context |
| com.amazonaws.services.s3.AmazonS3ClientBuilder | S3 client builder that enables building the S3 client with custom settings |
| com.workfusion.odf.ocr.client.OcrClient | base OCR client associated with the OCR instance specified in the WebHarvest context; can be injected using either the @OcrSdk11 or @OcrSdk12 qualifiers |
| com.workfusion.odf2.core.webharvest.service.ocr.OcrService | OCR service that provides a high-level Java API for communicating with the OCR instance; can be injected using either the @OcrSdk11 or @OcrSdk12 qualifiers |