wearefrank / ladybug

Enable users of your application to debug and test it
Apache License 2.0
12 stars 8 forks source link

Ladybug should show streaming and non streaming handling in the same way #166

Open jjansenvr opened 1 year ago

jjansenvr commented 1 year ago

The ladybug does not handle the input of streaming processing in the same way as non streaming, this is where it is not easy to understand for a developer or maintainer.

Functionally the flow is the same so it should look the same

gvanbrakel commented 1 year ago

Currently, handling an outputstream is handled in iaf-ladybug/IbisDebuggerAdvice.debugProvideOutputStream by the following code:

    /**
     * Provides advice for {@link IOutputStreamingSupport#provideOutputStream(PipeLineSession session, IForwardTarget next)}
     */
    public MessageOutputStream debugProvideOutputStream(ProceedingJoinPoint proceedingJoinPoint, PipeLineSession session) throws Throwable {
        if (!isEnabled()) {
            return (MessageOutputStream)proceedingJoinPoint.proceed();
        }
        String correlationId = getCorrelationId(session);
        if (log.isDebugEnabled()) log.debug("debugProvideOutputStream thread id ["+Thread.currentThread().getId()+"] thread name ["+Thread.currentThread().getName()+"] correlationId ["+correlationId+"]");
        if (proceedingJoinPoint.getTarget() instanceof ISender) {
            ISender sender = (ISender)proceedingJoinPoint.getTarget();
            // Use WriterPlaceHolder to make the contents that is later written to the MessageOutputStream appear as input of the Sender
            WriterPlaceHolder writerPlaceHolder = ibisDebugger.senderInput(sender, correlationId, new WriterPlaceHolder());
            MessageOutputStream resultStream = (MessageOutputStream)proceedingJoinPoint.proceed();
            String resultMessage = handleMessageOutputStream(writerPlaceHolder, resultStream);
            ibisDebugger.senderOutput(sender, correlationId, resultMessage);
            return resultStream;
        }
        if (proceedingJoinPoint.getTarget() instanceof IPipe) {
            IPipe pipe = (IPipe)proceedingJoinPoint.getTarget();
            PipeLine pipeLine = pipe instanceof AbstractPipe ? ((AbstractPipe)pipe).getPipeLine() : new PipeLine();
            // Use WriterPlaceHolder to make the contents that is later written to the MessageOutputStream appear as input of the Pipe
            WriterPlaceHolder writerPlaceHolder = ibisDebugger.pipeInput(pipeLine, pipe, correlationId, new WriterPlaceHolder());
            MessageOutputStream resultStream = (MessageOutputStream)proceedingJoinPoint.proceed();
            String resultMessage = handleMessageOutputStream(writerPlaceHolder, resultStream);
            ibisDebugger.pipeOutput(pipeLine, pipe, correlationId, resultMessage);
            return resultStream;
        }
        log.warn("Could not identify outputstream provider ["+proceedingJoinPoint.getTarget().getClass().getName()+"] as pipe or sender");
        return (MessageOutputStream)proceedingJoinPoint.proceed();
    }

In this code, an input and an output checkpoint is created within the context of the calling pipe or sender. This yields the nested pattern.

I would like to suggest two alternative checkpoint generating methods: provideOutputStreamToPipe() and provideOutputStreamToSender(). These methods should return a Function<MessageOuputStream,MessageOuputStream> that can be used to give ladybug control over the returned outputStream. The above code would then become:

        String correlationId = getCorrelationId(session);
        if (log.isDebugEnabled()) log.debug("debugProvideOutputStream thread id ["+Thread.currentThread().getId()+"] thread name ["+Thread.currentThread().getName()+"] correlationId ["+correlationId+"]");
        if (proceedingJoinPoint.getTarget() instanceof ISender) {
            ISender sender = (ISender)proceedingJoinPoint.getTarget();
            Function<MessageOutputStream,MessageOutputStream> outputStreamDebugger = ibisDebugger.provideOutputStreamToSender(sender, correlationId);
            MessageOutputStream resultStream = (MessageOutputStream)proceedingJoinPoint.proceed();
            return outputStreamDebugger.apply(resultStream);
        }
        if (proceedingJoinPoint.getTarget() instanceof IPipe) {
            IPipe pipe = (IPipe)proceedingJoinPoint.getTarget();
            PipeLine pipeLine = pipe instanceof AbstractPipe ? ((AbstractPipe)pipe).getPipeLine() : new PipeLine();
            Function<MessageOutputStream,MessageOutputStream> outputStreamDebugger = ibisDebugger.provideOutputStreamToPipe(pipe, correlationId);
            MessageOutputStream resultStream = (MessageOutputStream)proceedingJoinPoint.proceed();
            return outputStreamDebugger.apply(resultStream);
        }

Ladybug could then display the contents of the messageOutputStream as the result of the current pipe/sender, and as the input of the next pipe/sender. If the messageOutputStream was null, the contents could be ignored.