6th tutorial - adding logic


Table of contents
  1. Introduction
  2. Example project
  3. Extending the lottery game
    1. Spring config
    2. Additional java classes
    3. Extended application
    4. Running the application
  4. Conclusion

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.