In a project, I have a task scheduling service, which is allowed to execute certain public methods from other services. Each service decides itself, which methods it want to make available for the task scheduling service by annotating them with @Operation
. The task scheduling service simply will scan all given services for this annotation to get aware of methods it is allowed to be configured to execute. This means a user which wants to create a task will have a list of @Operation
methods presented.
Let's take the example following example of an update service (one service among a few)
public interface UpdateService extends Service{
@Operation
public void update(String updateFileName);
public List<String> getAvailableUpdateFileNames();
@Operation
public void ...
...
}
The way the UpdateService
interface is declared, a user may create a task over the task service executing the update
method. By letting the user freely configure a task with any string is mostly certain erroneous. The interface, in this case, therefore should present a list of predefined values, which can be used with the update
method.
As it is, the update service also knows a getter method which reveals all available update. This would be ideal as a source for the predefined values.
That's why I came with the following approach:
public interface UpdateService extends Service{
@Operation(sourceClass=UpdateService.class,sourceMethod="getAvailableUpdateFileNames")
public void update(String updateFileName);
public List<String> getAvailableUpdateFileNames();
}
The @Operation
interface is introduced to a couple parameters.
The task service still scans all given service interfaces for @Operation
annotations, to present a defined list to the user.
The Task manager also understands the arguments:
- sourceClass: This class has to contain the source method
- sourceMethod: This method returns values which can be used for the annotated interface function.
With the help of reflection, the task service executes the sourceMethod and will offer the returned values generically as predefined values for the task configuration of the update(String updateFileName)
method in the user interface. The user can select such a value and schedule the update
method with this selected value as an argument.
This example is very basic and currently would only work if the annotated method would have 1 argument. Although it would be completely generic and decoupled.
Is there a better way to solve this issue? Or isn't this approach that bad at all?
Service
interface but not theUpdateService
(Autowire). Through reflection, it still could achieve the wanted result.operations()
function that returns a list ofOperation
s, that hasmethod()
andarguments()
functions. Interfaces/classes are much more flexible than Annotations, especially since annotations have no behavior.Operational
to each service offering operations. In this interface I will have a simplegetOperations()
method. The Operation methods then can contain action objects implementing the actually function. Makes absolutly sense