6th tutorial - adding logic
Table of contents
Introduction
This tutorial covers extending an existing application with new functionality and support for new event types. The reader should be proficient in Java, maven, git, Spring and have completed the first lottery tutorial before starting this tutorial.
Our goal is to demonstrate how Fluxtion code generation makes it easy and cost-effective to add new functionality to an existing application with confidence.
At the end of this tutorial you should understand:
- How to add new event types and behaviours to user functions
- How to re-generate the container with new functionality
Example project
The example project is referenced in this tutorial.
Extending the lottery game
We will extend the lottery game to include a power lottery game and some additional reporting interfaces the application can use. The new java classes are built in the same style as previous tutorials with annotations marking the callbacks. To bind the new classes into the event processor we update the spring config file and run the build to regenerate the LotteryProcessor
Processing logic
Our design sketches show what we intend to integrate into our system
flowchart TB
classDef eventHandler color:#022e1f,fill:#aaa3ff,stroke:#000;
classDef graphNode color:#022e1f,fill:#00cfff,stroke:#000;
classDef exportedService color:#022e1f,fill:#aaa3ff,stroke:#000;
style EventProcessor fill:#e9ebe4,stroke:#333,stroke-width:1px
buyTicket><b>ServiceCalls</b>\n buyTicket, openStore, closeStore, setTicketSalesPublisher]:::eventHandler
selectWinningTicket><b>ServiceCalls</b>\n selectWinningTicket, setResultPublisher]:::eventHandler
isTicketSuccessful><b>ServiceCalls</b>\n isTicketSuccessful, publishReport]:::eventHandler
LotteryMachine([<b>ServiceLookup</b>::LotteryMachine]):::exportedService
TicketStore([<b>ServiceLookup</b>::TicketStore]):::exportedService
GameResultStore([<b>ServiceLookup</b>::GameResultStore]):::exportedService
TicketStoreNode[TicketStoreNode\n <b>ExportService</b>::TicketStore]:::graphNode
LotteryMachineNode[LotteryMachineNode\n <b>ExportService</b>::LotteryMachine]:::graphNode
PowerLotteryMachineNode[PowerLotteryMachineNode\n <b>ExportService</b>::LotteryMachine]:::graphNode
GameReportNode[GameReportNode\n <b>ExportService</b>::GameResultStore]:::graphNode
selectWinningTicket ---> LotteryMachine
buyTicket --> TicketStore
isTicketSuccessful --> GameResultStore
LotteryMachine ---> LotteryMachineNode & PowerLotteryMachineNode
TicketStore ---> TicketStoreNode
GameResultStore --> GameReportNode
subgraph EventProcessor
TicketStoreNode --> LotteryMachineNode & PowerLotteryMachineNode --> GameReportNode
end
Spring config
The spring config is updated to include the new beans in the event processor
<beans>
<bean id="ticketStore" class="com.fluxtion.example.cookbook.lottery.nodes.TicketStoreNode">
</bean>
<bean id="lotteryMachine" class="com.fluxtion.example.cookbook.lottery.nodes.LotteryMachineNode">
<constructor-arg ref="ticketStore"/>
</bean>
<bean id="powerMachine" class="com.fluxtion.example.cookbook.lottery.nodes.PowerLotteryMachine">
<constructor-arg ref="ticketStore"/>
</bean>
<bean id="gameReport" class="com.fluxtion.example.cookbook.lottery.nodes.GameReportNode">
<constructor-arg index="0" ref="lotteryMachine"/>
<constructor-arg index="1" ref="powerMachine"/>
</bean>
</beans>
Additional java classes
Our additional classes to add are:
public interface GameResultStore {
boolean isTicketSuccessful(Ticket ticket, Consumer<Boolean> responseReceiver);
boolean publishReport(Consumer<String> reportReceiver);
}
public record WinningTicketReport(Ticket powerLotteryTicket, int winningNumbers) { }
@Slf4j
public class PowerLotteryMachine extends LotteryMachineNode implements @ExportService LotteryMachine {
public PowerLotteryMachine(Supplier<Ticket> ticketSupplier) {
super(ticketSupplier);
}
@Override
public void selectWinningTicket() {
//removed for clarity...
}
}
public class GameReportNode implements @ExportService GameResultStore, @ExportService LotteryMachine {
private final LotteryMachineNode lotteryMachine;
private final PowerLotteryMachine powerLotteryMachine;
private Consumer<String> resultPublisher;
private int gameCount;
public GameReportNode(
@AssignToField("lotteryMachine") LotteryMachineNode lotteryMachine,
@AssignToField("powerLotteryMachine") PowerLotteryMachine powerLotteryMachine) {
this.lotteryMachine = lotteryMachine;
this.powerLotteryMachine = powerLotteryMachine;
}
@Override
public boolean isTicketSuccessful(Ticket ticket, Consumer<Boolean> responseReceiver) {//removed for clarity}
@Override
public boolean publishReport(Consumer<String> reportReceiver) {//removed for clarity}
@Override
public void selectWinningTicket() {// store results}
@Override
public void setResultPublisher(Consumer<String> resultPublisher) {//publish report}
@Override
public void newGame() {
gameCount++;
}
}
Extended application
Now the new functionality and services are bound in to the generated event processor, the application can be extended to use the new features in the sample main. Client code is completely hidden from the updates and the new wiring that has been generated. All the previous functionality works as before and the application behaviour is extended reliably and with teh minimum of effort.
public class LotteryApp {
private static LotteryMachine lotteryMachine;
private static TicketStore ticketStore;
private static GameResultStore resultStore;
public static void main(String[] args) {
start(LotteryApp::ticketReceipt, LotteryApp::lotteryResult);
lotteryMachine.newGame();
ticketStore.openStore();
//open store and buy ticket
Ticket myGoldenTicket = new Ticket(12_65_56);
ticketStore.buyTicket(myGoldenTicket);
ticketStore.buyTicket(new Ticket(36_58_58));
ticketStore.buyTicket(new Ticket(73_00_12));
//run the lottery
lotteryMachine.selectWinningTicket();
//our new functionality
resultStore.isTicketSuccessful(
myGoldenTicket,
b -> System.out.println( "\n" + myGoldenTicket + " is a " + (b ? "WINNER :)\n" : "LOSER :(\n")));
resultStore.publishReport(System.out::println);
}
public static void start(Consumer<String> ticketReceiptHandler, Consumer<String> resultsPublisher){
var lotteryEventProcessor = new LotteryProcessor();
lotteryEventProcessor.init();
//get service interfaces
lotteryMachine = lotteryEventProcessor.getExportedService();
ticketStore = lotteryEventProcessor.getExportedService();
resultStore = lotteryEventProcessor.getExportedService();
//register listeners via service interface
lotteryMachine.setResultPublisher(resultsPublisher);
ticketStore.setTicketSalesPublisher(ticketReceiptHandler);
//start the processor
lotteryEventProcessor.start();
}
public static void ticketReceipt(String receipt){
System.out.println(receipt);
}
public static void lotteryResult(String receipt){
System.out.println(receipt);
}
}
Running the application
Executing the application produces the following output:
good luck with Ticket[number=126556, id=c5ad2b74-f9cf-4bbb-92d7-4ac9eb76e129]
good luck with Ticket[number=365858, id=a4f3dfa7-2e6d-4689-94e3-72678be3c68b]
good luck with Ticket[number=730012, id=ea6f5b9c-8571-4547-93f7-8752ee6b19df]
winning numbers:730012
POWER-LOTTERY winning numbers:126556
Ticket[number=126556, id=c5ad2b74-f9cf-4bbb-92d7-4ac9eb76e129] is a WINNER :)
GAME REPORT gameNumber:1
lottery winner:Ticket[number=730012, id=ea6f5b9c-8571-4547-93f7-8752ee6b19df]
POWER-LOTTERY winner:Ticket[number=126556, id=c5ad2b74-f9cf-4bbb-92d7-4ac9eb76e129]
Process finished with exit code 0
Conclusion
Hopefully you have learnt that extending an application with Fluxtion is simple and saves a lot of developer time. Existing behaviour is left untouched the automated generation removes many potential errors. All the developer effort is focused on the adding business logic and creating value.
Comparing the previous generated version with the new version gives the developer to validate the new application classes are wired in as expected.