Table of Contents



1. Introduction

snippet 1.1

snippet 1.2

snippet 1.3

myService

snippet 1.4

preStuff

postStuff

i

do-layer-i+1

snippet 1.5

onDone

postStuff

snippet 1.6

snippet 1.7

The Problem

snippet 1.8

do-layer-i+1

onDone

do-layer-i+1

snippet 1.9

Our Approach

2. The Proposed Coding Style

snippet 2.1

Stage_m

R_n

R_m

R_n , T_n and v_n represent the return type, argument types and argument variables of Stage_n , as exposed by its invoke method.

, and represent the return type, argument types and argument variables of , as exposed by its method. R_m , T_m and v_m represent the return type, argument types and argument variables of the subsequent Stage_m , as exposed by its invoke method.

EP are exceptions that pass through the layer shown;

are exceptions that pass through the layer shown; EC are exceptions that are caught in the layer shown;

are exceptions that are caught in the layer shown; EI are exceptions newly introduced in invoke , but outside the try block;

are exceptions newly introduced in , but outside the block; ES are exceptions newly introduced (and not caught) anywhere in the layer shown.

Accordingly, the subsequent Stage_m . invoke throws the combination of EP and EC , whereas Stage_n . invoke throws the combination of EP , EI , and ES . Note that there is no loss of generality in this representation.





invoke

body

FullStage

invoke

getStage

FullStage

invoke()

invoke()

getStage

Class

getStage

chain.setRedirector()

Redirector

invoke

chain.setRedirector()

tage.invoke()

chain.setRedirector(null)

midStuff

The Proxy Trick

body

invoke()

Stage_n

body

invoke

complete-layer-i

body

invoke

body

body

body

invoke

body

body

invoke

body

getStage

setRedirector

body

invoke

diagram 2.1

diagram 2.2

stage.invoke()

RedirectedException

RedirectedException

invoke()

body()

Details of the Support Library Interface

Chain

Chain

invoke

Chain

invoke

body

FullStage

getStage

StageInvocation

StageBody

invoke

body

invoke

body

Redirector

Chain.setRedirector

Redirector

IOException

snippet 2.2

stage.invoke()

Stage_m.invoke

IOException

Stage_m.invoke

IOException

setRedirector

setRedirector

stage.invoke()

setRedirector

snippet 2.3

WriteRedirector

Redirector.With1<IOException>

snippet 2.4

WriteRedirector

launch

onCompleted

onException1(IOException)

onUnexpectedException

onUnexpectedException

RuntimeException

Redirector.With2<E1, E2>

setRedirector

stage.invoke()

IOException

stage.invoke()

IOException

Stage_m.invoke

stage.invoke()

RedirectedException

IOException

Stage_n.invoke

body()

IOException

Stage_n.body

setRedirector

setRedirector

redirectAndClose

chain.redirectAndClose(myRedirector)

setRedirector

invoke(chain)

redirectAndReturn

redirectAndClose

redirectAndReturn

setRedirector

snippet 2.5

startChain

FirstStage

ChainOutcomeListener

Chain.startChain

Chain

RedirectedException

A Broad Definition

snippet 2.6

layer-i

preStuff

do-layer-i+1

body

a

do-layer-i

postStuff

a

c

b

snippet 2.7

invoke

body

invoke

stage.invoke()

body()

There should be no instructions preceding any call of body() in the invoke method. This is because any such instruction would be supposed to be executed before body , but, actually, it would be executed after, in the backward chain.

However, the constraint could be relieved by considering that this code misplacement would change the semantics of the program only if side-effects or early returns were involved. So, any initialization code with no such side-effects is actually allowed.

Let’s call the above the Body Constraint.

There should be no further instructions following any call of stage.invoke() for the next Stage in the body method (and similarly for the FirstStage.invoke method).

This is because the control flow could proceed asynchronously after that call. By the way, in that case, stage.invoke() would issue a RedirectedException and any subsequent instruction would be cut off.

The only instruction supported after stage.invoke() is the return of its result (when it is not void), as shown in the base template; this is why the return type of body was constrained to be the same as the included sub-Stage’s invoke . If invoke is not void, ignoring its result (to yield a void body ) is also supported.

However, the actual code that, after stage.invoke() , causes the control flow to exit the method and to possibly return the result is free, provided, again, that no side effects are involved.

Let’s call the above the Substage Constraint.

body

postStuff

invoke

body()

postStuff

3. Notes on the Usability of the Technique

missing initializationStuff at the beginning; missing midStuff after an external API call; lack of support for multiple sublayers and/or external API calls in sequence; coding limitations on preStuff ; no sharing of variables between preStuff and the subsequent parts of the layer; lack of support for invoking sublayers and/or external API calls outside the try block.

initializationStuff

snippet 3.1

initializationStuff

setRedirector

stage.invoke

body

Common Initialization for a Layer

initializationStuff

initializationStuff

invoke

snippet 3.2

Passing Values Back from the Body

invoke

preStuff

body

invoke

body()

vi

postStuff

body()

body

invoke

body

invoke

invoke

body

stage.invoke()

Passing Values Back from the Sub-Stage

stage.invoke()

invoke

body

invoke

Redirector

stage.invoke()

invoke

Early Return from a Layer

preStuff

postStuff

completionStuff

body

body

postStuff

completionStuff

body

invoke

4. Notes on the Overhead Introduced

body

getProxy

PatternException

invoke

body

java.lang.reflect.Method.invoke

body

invoke

5. Extensions to the Basic Rules

Simplified Syntax for Simple Layers

snippet 5.1

invoke

body

do-layer-i

snippet 5.2

invoke

invoke

invoke

SimpleStage

FullStage

invoke

Direct Access to the Backward Chain

initializationStuff

snippet 5.3

ChainOutcomeListener

startChain

snippet 5.4

SimpleStage

onClose

addClosingHook

invoke

SimpleStage

body

FullStage

invoke

FullStage

Resuming the Chain From Within catch and finally Blocks

body

invoke

body

snippet 5.5

invoke

There can be no further instructions following a call of stage.invoke() for the next Stage in the invoke method.

This is because the control flow could proceed asynchronously after that point. So, any subsequent instruction executed by the method (for instance, a finally block) might occur at the wrong time.

The only instruction supported after stage.invoke() , unless it occurs in the finally block, is the return of its result (when it is not void); this would constrain the return type of invoke to be the same as the included sub-Stage’s invoke . Ignoring a result (to yield a void invoke ) is also supported.

However, the actual code that causes the control flow to exit the method and to possibly return the value is free, provided, again, that no side effects are involved.

Let’s call this the Resume Constraint.

stage.invoke()

return;

stage.invoke()

stage.invoke()

Multiple catch blocks may terminate with the launch of a new Stage, but if there is at least one, then there cannot be the finally block, nor any completionStuff .

blocks may terminate with the launch of a new Stage, but if there is at least one, then there cannot be the block, nor any . If a finally block terminates with the launch of a new Stage, then there cannot be any completionStuff . Moreover, as said, no explicit return statement is allowed for leaving the block. As a consequence, any value returned by stage.invoke should be ignored; but, actually, a stage.invoke method that returns a value is not supported at all; it is only allowed to declare a void return type.

block terminates with the launch of a new Stage, then there cannot be any . Moreover, as said, no explicit statement is allowed for leaving the block. As a consequence, any value returned by should be ignored; but, actually, a method that returns a value is not supported at all; it is only allowed to declare a return type. Launching a new stage at the end of the completionStuff is fully supported.

is fully supported. Launching a new Stage in the postStuff is not a significant case (there would have to be no try-catch-finally statement at all and no completionStuff ; this would end up to be the same

case as launching a new stage in the completionStuff ).

is not a significant case (there would have to be no statement at all and no ; this would end up to be the same case as launching a new stage in the ). Launching a new Stage in the initializationStuff is still not allowed by the Body Constraint.

Stage.invoke

invoke

stage.finallyCheck()

PatternException

invoke

invoke

body

6. A Non-trivial Example

getting a socket just opened; reading a request; parsing the request; waiting for some time, if needed (let's suppose that each user is granted a limited number of requests per second); invoking an external service; writing back the response.

The code should also manage several exception conditions, which can occur in the various tasks, by writing back proper error notifications.

snippet 6.1

snippet 6.2

PoolRedirector, TimerRedirector, AsynchReader,

AsynchWriter

MyServiceExecutor

7. Conclusion

Acknowledgements

Dario Crivelli

Dario Crivelli is a Computer Scientist at Weswit, the company behind Lightstreamer, an award-winning product for real-time data delivery over the Internet. Dario has been the lead developer of Lightstreamer Server since the beginning. Dario Crivelli is a Computer Scientist at Weswit, the company behind Lightstreamer, an award-winning product for real-time data delivery over the Internet. Dario has been the lead developer of Lightstreamer Server since the beginning.

Hierarchical exception handling and static exception checking are among the most significant features offered by the Java language; however, their support is limited to procedural code and can only be exploited in hierarchies of function calls. So, there is nothing similar to assist us when it comes to event-driven programming or asynchronous programming, where the work is scattered among many functions that are independent from one another. This is particularly frustrating in some architectures, where the logical design is strictly hierarchical and the control flow is actually sequential, but asynchronous API calls are involved.The present article deals with the latter case; we show how, by writing code that follows a specific pattern and leans on a suitable support library, the benefits of hierarchical exception handling and static exception checking can be achieved. This technique was applied in some parts of Lightstreamer Server with proficiency.Asynchronous programming is a widely adopted technique for the implementation of web services and it is almost mandatory for providing push notifications based on acommunication channel. In a nutshell, the technique consists in never using blocking API functions; whenever a wait for a condition is needed at some point in the execution flow, all the remaining part of the processing should be collected in one or multiple callback functions and supplied to a proper asynchronous API function. Therefore, the wait will arise from the delayed invocation of the callback. In pseudocode terms, this means avoiding the form:in favour of the form:Hopefully, the non-blocking API will reduce the need for threads (by causing, in a certain way, multiple execution flows, when in waiting state, to share a single blocked thread); this will lead to a better usage of system resources and improve the overall scalability.By the way, the use of blocking API functions, if a non-blocking equivalent is not available, can be turned into the asynchronous paradigm by wrapping them in dedicated threads or in a thread pool, as in:which mimics snippet 1.2 . The use of a thread pool would allow us to configure it in such a way as to pose an upper limit to the resource consumption (in terms of blocked threads) related with, as in the well-known. This is certainly an aspect that a sensible asynchronous library, if available, would have taken into account.The drawback of the asynchronous approach is that the program code has to be spread over multiple callbacks, which affects code readability.This is particularly true when, from a logical point of view, the execution flow is strictly sequential and lends itself to arepresentation, made of multiple layers, such as in the following pseudocode:whereandare both related with layerscope and should share the same context, hence should be defined as close as possible. If, anywhere within the next layer, the invocation of an asynchronous API were possible, then the above form would be incorrect, as the wholefunction would have to be considered as asynchronous too; hence, the above pseudocode would have to be transformed into the more complicated form:where anparameter is added to each layer to carry forward theinstructions in a recursive way. As said, some of these layers may involve the invocation of an external API. We can take this into account by extending snippet 1.4 into this general form:whose asynchronous equivalent would become:This code style is quite cumbersome, yet many programmers can use it with proficiency.However, by now focusing on thelanguage, the above transformation would deprive us of one of the most useful tools provided by this language: static exception checking. Top-down style code can strongly benefit from static exception checking, which allows different error conditions to be traced back and handled at the proper layer.Hence, sticking to pseudocode, let’s assume that a typical layer has the form:Where, by, we represent a new layer (the extended case that includes an external API invocation, as in snippet 1.6 , could be considered as well). This form does not have a natural equivalent in the asynchronous case: by providing the next layer with all the handlers in form of callbacks, we would lose the compiler check that each type of exception must be either handled or declared.Or, we can put it in this way: theoperation to be carried forward toshould be the wholecheck, whose evaluation should be postponed and applied only when the outcome of the asynchronous processing of the try block is determined; something like this:Of course, writing multiple layers in a style like this would be awkward.When codingin Java, we did face the need to implement top-down logical workflows, which included the invocation of asynchronous APIs, but also a great deal of exception handling, in the most clear, error-safe, and manageable way.So, we decided to adopt the above pseudocode pattern, to ensure correct exception propagation across asynchronous invocations, but struggled to find the best way to write code equivalent to that. The resulting code would feature a separation between lines related with functional aspects and boilerplate lines, related with flow control aspects. In particular, the latter would be similar across the various layers, hence easily recognizable and prone to be just ignored by a knowledgeable programmer when concentrating on functional aspects.We eventually came up with a bunch of Java support code that allowed us to write our layers in a, which, in our view, achieves a reasonable compromise between code readability and syntax richness.The produced code consists in a, which supplies various classes, required by the coding form, which perform all the needed tricks under the hood, and in a further library of, which wrap all asynchronous APIs to be used, to make them compliant with our coding form.We will refer to this coding form and to this technique in general as theThe goal of the remaining part of this post is to illustrate the coding style made possible by our support library and to discuss the related pros and cons; and hopefully, receive some feedback about the approach as well.On the other hand, a presentation and a description of the support library and utility classes is out of the current scope: in the following code examples, we will take them for granted and just describe shortly their behavior.The form in which we write each layer in a possibly long sequence, according to the Asynchronous Top-Down Chain pattern, is the following:which, in turn, assumes a similar definition for the subsequentNote that we keep using function calls to represent (possibly empty) sequences of instructions. Also types, variables, and exceptions (with relatedblocks) indicated are placeholders for (possibly empty) lists of them; obviously, this does not hold forand, the return types of the methods, which however, may represent either a real type orand, in the latter case, the code shown should be adjusted accordingly. As can be inferred from the code, our naming convention is the following:On the other hand, for the exceptions, we use a different convention, as:As you can see, we introduce, for each layer, a dedicated class, which we call aclass. This class contains the implementation of the layer through theandmethods and must inherit from the suppliedclass. The execution of the layer involves the creation of an instance of the Stage class and the call of themethod.However, the creation of an instance is only possible through the library’smethod (to enforce this constraint, the class inherits froma dummy abstract method that is not visible from application code; hence, the Stage class cannot implement it and must be declared). This allows our library to return, instead of an instance of the Stage class, an instance of a suitableclass (which extends and instruments the Stage class), so that the invocation of the layer throughcalls the proxy instead. In this way, upon, the proxy can take care of all the stuff needed to execute the layer in the asynchronous style depicted in snippet 1.9 .To enable the instance generation, the Stage class must declare a suitable constructor, which must be. On the other hand, the Stage class is not required to provide an empty constructor: themethod accepts any arguments after the Stage’sspecification and looks for a corresponding constructor for these arguments. This even supports Stage classes defined as non-static inner classes, provided that a pointer to an instance of the outer class is supplied tobefore any other constructor arguments. Obviously, all the conditions involved will not be checked until runtime and exceptions may arise.Invocations of asynchronous APIs are carried out in a similar way. This is accomplished with the optionalcall, where different implementations of theinterface can encapsulate the various asynchronous APIs available. When specifying ajust before calling the sub-Stage’s, the proxy will take care of invoking the external API through theand then the subsequent sublayer in the proper way. So, through a, the proxy may call an asynchronous API, for instance, to write something, then proceed with the next layer when finished. Another typical case is using afor scheduling the next layer for running in a specified thread pool.It is important thatimmediately preceeds s, because the two calls are strongly coupled. More precisely, it is important that no exceptions or early returns can occur in between; otherwise, it will be the responsibility of the application to ensure thesetting is reset (by calling).Note that, by comparing this form with snippet 1.6 , the(i.e., code to be executed after the invocation of an external API but not pertaining to a sublayer) is missing; this and other expressivity limitations of the proposed coding rules will be discussed later.Providing general-purposeis the job of the utility classes introduced above, although customcan be defined as well.By the way, we chose to use the word “Stage" to qualify layer implementation classes, to recall the SEDA approach. We will also say “Stage” to indicate a layer as implemented in Java through our coding rules.A key part of the technique is the segregation of all the code up to the invocation of the next Stage in its own method (that we name), so the method can be overridden by the proxy as well.The rationale is that, upon a call to the proxy’s(here, think to an invocation ofas defined in snippet 2.1 ), only the Stage’smethod will be executed instead, as in snippet 1.9 above. Hence, the Stage’smethod (likein snippet 1.9 ) has to be kept and performed only after the real termination of, in whichever thread it occurs; but, in this case,may not callagain: it should only recall the outcome of theexecution (or the exception possibly thrown) and this is what the proxy’s overridingmethod allows for and it is supposed to do.A condition for the application of this trick is that theandmethods share the same argument types, though not necessarily the same exceptions and the same return type. This can be enforced and managed by our support library, as will be detailed below.As a consequence of the trick, the whole execution of the chain of Stages consists in a, made of theexecutions, followed by a, made of theexecutions.The backward chain will be played by the library upon the first time amethod exits without calling the next Stage.We chose this form, despite the complexity it introduces, because it features a key property:; in fact, if justreturned an instance of the Stage class instead of a proxy andshould directly invoke the involved external API in a synchronous way, then the whole execution would be sequential. In practice, as far as functional aspects are concerned, we can read the code as though it was synchronous with the only added complexity as a result of the separation offrom, not a big issue indeed.The following sequence diagrams illustrate the work of the proxies. Diagram 2.1 shows two Stages, S1 and S2, where S1 invokes S2 on a different thread, as exemplified by the asynchronous message 2.1.2.1. The corresponding synchronous equivalent, where proxies are bypassed, is shown next, in diagram 2.2 .Whenredirects the execution of the chain on a different thread, because of a Redirector specified there or in any sub-Stage (as exemplified by arrows 2.1.2.2 to 2.3 in diagram 2.1 ), the call throws a. This exception should never be caught directly by application code, but it should be allowed to flow back to the caller recursively, as it signals that the current thread is no longer the one that is processing the chain. By the way, note that the library may throw aonly upon, and never upon; as a consequence, you can ignore it completely when writing yourstatements.The various classes silently used in snippet 2.1 take care of doing all the needed tricks.In particular, theclass is the heart of the implementation. An instance of theclass has to be created at the start of the chain processing, then forwarded from each Stage to the next, to keep track of the stack ofmethods that make up the backward chain. Theinstance collects the needed information in collaboration with the proxies. It is mandatory that the last parameter in all the customandmethods carries the involved Chain instance.Thesuperclass provides the needed background for the Stage classes. In particular, it provides thefactory method and theandannotation classes, which simplify the definition of the Stage class; in fact, the library identifies theandmethods by their annotations, not by their names, which are actually free.As anticipated, the proxies carry out two jobs: one is decoupling theandmethods, the other is launching the next Stage through an asynchronous API, when needed, as specified through theinstance supplied to the optional. One point that we have not yet considered is that the execution of an asynchronous API may give rise to its own error conditions, which could pop up asynchronously as well and any of these exceptions have to be handled with suitable callbacks. This complication affects asynchronous programming techniques in general and we also avoided coping with it by extending snippet 1.9 (we just left that to the reader’s imagination).The Asynchronous Top-Down Chain pattern deals with this aspect by providing extended versions of theinterface.To show an example, we consider the call of an asynchronous write operation, after which the processing can continue, unless the write fails with anThe support library allows us to express that as:but, in this case, the call to, in addition to the exceptions pertaining to themethod, may also cause anto be issued to the caller. However, the exception declaration ofmight not include an; so, to enforce the additional declaration, our trick is to have it declared by thecall instead (note thatmust always be followed byimmediately). This is achieved by the support library by defining the following overload ofwhere, which can be supplied by the utility class library, must be written by extending, which is based on the following API:In, themethod should start the asynchronous write operation, then take care of ensuring that exactly one among the provided, andis eventually called.Note themethod, which allows unchecked exceptions occurred in the processing to be managed; it may be used to forward checked exceptions as well, but in this case, they will get wrapped by aWith the same idea, any other asynchronous API can be encapsulated.By the way, allowing this trick is the most frustrating part of the support library coding, because it requires the library to prepare different classes for each different number of exceptions to be expected (soand so on), along with all the related overloads ofOne clarification may be useful at this point. In the example above, we said that a call towould “also cause anto be issued to the caller”. This does not mean thatmay throw aneven though thesignature does not allow it. Actually, because ais involved, the proxy will causeto just throw a, as explained in the previous paragraph.A possiblewould then reappear in a different thread, as the library initiates the backward chain with a call to the outer: there, the includedinvocation will throw theand this will be compliant with thesignature; in fact, this is exactly what the throws clause inhas forced us to do.In some cases, the invocation of the asynchronous API through ais the last action of the chain. But becausemust be followed by a sub-Stage call, we should have to supply a no-op Stage, whose proxy would just call theand then initiate the backward chain.This turns out to occur quite often, so, to simplify, a dedicatedmethod calledis provided, which gets aas argument. So, issuingreplaces both theand the subsequentcall on a no-op Stage. A similar method,, in which the no-op Stage just returns a supplied value is also provided.To support any kind of, the various exception-enabled overloads ofandare provided as well, similarly to what was done forSo far, we have shown the recursive part of a top-down chain of layers. We also have to provide a starting point. For this, we stick to the classic callback-based form:In fact, the staticmethod and theandinterfaces are the only means provided by the support library to initiate a chain (note that an optional logger for the chain’s own logging is also provided here). Themethod is the only place where ainstance is created and a possibleis eventually caught.The coding form shown in snippet 2.1 is very limited and does not allow for the conversion of all possible logical top-town representations of a workflow. In pseudocode terms, it roughly corresponds to the following:where, if the invocation of the next layer is omitted,will represent the innermost layer. The informal restrictions stated forare needed here to ensure that the whole block, up to and including the invocation of, can be segregated into a separatefunction (note that “does not change” only prevents the change of theargument values, not of anything pointed by these values). For the same reason,is only allowed to useandbut notOn the other hand, the starting point of the chain corresponds to a logical layer that propagates no exceptions, so that its invocation can fit into the form:In practice, the coding will only be straightforward if you can devise your logical layers in such a way as to fit into the above templates.Presumably, this will occur in few cases, whereas, in most cases, there won’t be a natural fit. In some cases, this might be achieved by refactoring the layers, possibly splitting some layers into an inner and an outer part, but at the cost of separating pieces of code that should better be placed close to each other (more on this later).However, the template shown in snippet 2.1 was only supposed to be an example and it does not exhaust the whole range of code that can take advantage of our support library. Actually, we would prefer to include in the Asynchronous Top-Down Chain pattern a wider range of possible code, still consistent with the support library, but, unfortunately, we are not able to provide a wider definition of the pattern in terms of formal coding rules.Nevertheless, once the behavior of the support library is known, along with all the impacts on code semantics, we could just take the library “as is” and design our code by directly leveraging the library for the intended purpose. This might turn out to be the easiest way to proceed in a real scenario and it will also help us overcome the limitations of the basic template.To resume, all we need is to ensure that the support library can operate the proxy trick (that is, the replacement of the calls towith calls toand the deferring of the real calls toin the backward chain) by keeping the same semantics as in a proxy-free case.In particular, claiming thatwould be the “last instruction of the block” or placing no instructions beforeserved this purpose but it was quite restrictive. The same conditions can be put in a lighter way; all told, our use of proxies poses two main constraints to the final code:This allows for a more flexible interpretation of the basic template.As a first consequence, according with the Substage Constraint, themethod can contain loops and branches, which may give rise to multiple places in the code in which an invocation of a further Stage is performed, only provided that any such invocation can only occur just before exiting the method. Obviously, some branches may terminate without calling a further Stage, which would end the forward chain processing.As another consequence, even the restriction that theexecuted after the sub- Stage should always be the same can be partly overcome, by placing branches in theblock of, leading to multiple alternative occurrences ofand. In fact, in many cases it could be possible to writestatements in the try block in such a way that the Body Constraint would still be satisfied.With the above considerations in mind, the main problematic points will now be analyzed in detail.To what extent can an arbitrary top-down algorithm be made to fit into the Asynchronous Top- Down Chain coding pattern? With reference to snippet 2.6 above, the form of the layers is quite limited, as several parts are missing.We can’t address the problem in formal terms here, but we can enumerate the missing parts in the proposed layer structure as follows:Actually, none of these limitations is a showstopper. Points 4 and 5 can be overcome by rewriting the code properly, whereas the missing parts in points 1, 2, and 3 can be, in principle, resumed by splitting the layer into two or more cascading layers.To see an example of splitting, if some(i.e., code preceding thestatement; see pseudocode snippet 1.8 ) were needed, we could spawn a previous, yet very simple, layer, as in:Splitting the layers in this way, obviously, worsens the code structure; in this case, we separate thefrom the other closely-related instructions, which is certainly less readable. Moreover, the separation may give rise to more occurrences of points 4 and 5 above to be overcome.Point 6 remains to be dealt with. This is an important issue, because, at least, the need to invoke external APIs in aorblock is something to be expected (for instance, to write a proper response to the client upon catching an exception).Surprisingly enough, there is no such limitation at all. We haven’t introduced that in snippet 2.1 and snippet 2.6 in order to keep them simple, but the use ofandis not limited to inside. We will cover this topic and provide all the details in a dedicated section later.All told, we think we can claim that the limitations of the Asynchronous Top-Down Chain pattern are not in terms of what can or cannot be expressed with it, but rather, in terms of what can be expressed in an acceptable form. It might be viewed as encouraging the fact that, for’s needs, the fit to this coding pattern was quite straightforward.We will now outline various common coding cases, to see how they can be handled best. Whoever is confident on his ability to produce code compliant with the Body and Substage Constraints can skip the remaining part of this chapter.As discussed above, a layer cannot include anyinstructions, as this is forbidden by the Body Constraint. Such initialization code can be useful to provide values that can be shared by all parts of thestatement. It also allows for any preparation stuff that should not be closed by theblock when not successful.As a corollary, the recently introducedJava language statement is not supported directly by the Asynchronous Top-Down Chain pattern; it would need to be expanded and, eventually, this might also give rise to the situation described here.We have seen how such initialization can be spawn in a previous layer.However, thecan also be useful to setup variables before entering thestatement; such variables would be handy, to allow theblock to pass values to the catch andblocks and, similarly, to allow ablock to pass values to theblock. Such variables should be declared as part of the current layer, not a previous one.Fortunately, as anticipated above, the Body Constraint allows simple local variable declarations before the try block and adding initializers to them is also compliant, provided that they are simple enough not to involve any side-effect or exception. To summarize, the initializers should be constants or, at most, they may only depend on the function arguments, but not on anything pointed to by function arguments; for instance, they might be numerical operations on numerical function arguments.This should account for most of the needs.The extended form of the consequentmethod is shown below:Setting shared variables instill does not allow for passing values back from theand the sublayer, to be used in theblock (probably the most important cases). In fact, these parts are segregated in themethod, so they cannot share local variables with the other parts in themethod.This need can be for the greatest part fulfilled by leveraging the return value of, that can be assigned to the commonin. Otherwise, side-effects in theexecution can be used.The simplest way is to use instance variables of the Stage class itself, to be written inand read in; the drawback is that a similar Stage class will not be reentrant and the allocation of a dedicated instance may be needed for each execution.Alternatively, bean objects could be supplied as arguments toby, so that the former will set values and the latter will get them; here, the problem is thatis required to supply its own arguments to, hence the beans should be created by the previous Stage, to be passed to, which may be out of context at that point.To pass values back from a sub-Stage, similar considerations apply. When leveraging the sub- Stage’sreturn value is not enough, supplying bean objects tomight be the best way; but, because this occurs within, this would only cover half of the trip: these beans would still have to be made visible also to, hence falling into the previous case.Getting values generated by an invocation of an asynchronous API may be needed as well (for instance, upon a socket read operation). The providedinterface does not support this, which means that the specificimplementation used must take care of providing its own interface extension to extract values generated in the call.These values can be made available to the subsequent sub-Stage, by passing a reference to theinstance itself to; to make them available also to the current stage’s, the reference to the Redirector instance can be passed back in any of the ways discussed above.In a general case (see snippet 1.8 ), theinstructions may issue astatement to leave the method successfully without calling the next layer and this would also cause anyandto be skipped. This cannot be achieved in our structure, where any return statement withincannot be distinguished from a normal termination of theblock, soandwould not be skipped.Such a behavior, if needed, could be implemented manually by passing flags back fromtoas seen above. For more complex cases, it could even be necessary to spawn intermediate Stages.The code structure resulting from the application of the Asynchronous Top-Down Chain pattern introduces several redundancies with respect to the logical form.We consider them to be acceptable, as they don't compromise readability.For instance, the introduction of one Stage class for each layer may even improve readability, by emphasizing the staged structure.The main complication introduced is the spawning of amethod, which separates most or all of theblock from the other parts of the Stage processing. This, along with the possible splitting of layers discussed in the previous paragraph, gives rise to a scattering of code fragments that negatively affects code readability. When multiple Stage classes are needed to implement a complex layer, we suggest defining them as inner classes of a hosting class, to keep them close to each other; as already pointed out,supports even non-static inner classes.The complication as a result of the code scattering is also mitigated by the fact that the correct forwarding of parameters from Stage to Stage is checked by the compiler. And the same holds for exception forwarding. But if you consider that this, actually, applies to asynchronous code, then this property, rather than just a compensation, should be regarded as a worthwhile benefit.In general, the use of boilerplate code is affordable when it is just a hassle, but it does not introduce risks of more coding errors. In this case, there are many constraints that have to be satisfied to produce correct code, each of which is a potential cause of errors.Most of these constraints are directly related with the use of the support library.The library interface has been expressly designed with the aim of limiting the freedom of its use, so that illegal uses can be detected by compiler checks.But other kinds of illegal uses could not be expressed in form of syntax constraints. Many of them are checked by the library at runtime; detected errors give rise to a, which is of an unchecked type, or to error-level log. The library does its utmost to detect uncompliant code as early as possible and we expect syntax related issues to be found on the first testing run of the code.Still, some constraints are under the sole control of the coder and this mainly regards the fact that theandmethods must have the required form.In particular, it is exclusive responsibility of the coder to ensure that the Body Constraint and the Substage Constraint are satisfied. Failing to do so may cause unexpected behavior that may prove difficult to detect.The runtime overhead is potentially significant because of the use of reflection methods, but we have not found important impacts on the overall performance in our measurements.Note that the support library has been designed in such a way that the generation of the synthetic proxy classes is done only once for each Stage class. And this operation is supposed to be the heaviest one involving the reflection package.By the way, for the generation of the proxy classes, our library takes advantage of the Javassist library. This allowed us to come out with relatively simple coding rules. More complicated but still useful formulations of our pattern could be possible by leaning on the Java reflection package only, or even without any reflection involved.The runtime overhead added upon each Stage method call consists mainly in a call to. The most annoying aspect of this extra work is probably that it is visible when tracing the execution of the chain step by step through a programming GUI for debugging purpose. It may be advisable that both JDK reflection classes and the support library are configured to be hidden from the tracing. Similarly, the extra calls will overload stack traces and stack dumps.Some memory overhead is also added, because of the various utility objects needed. However, the support library was crafted with the aim of mitigating this overhead too.For instance, it is not needed to create a new Stage implementation object upon each invocation of the Stage; on-the-fly creation was used in the basic snippet 2.1 , but only for brevity.In fact, as far as the support library is concerned, Stage implementation objects can be reused by multiple subsequent executions and even used concurrently by multiple threads. So, the level of sharing of the Stage implementation objects is up to their coding; you can choose to use a single static stateless instance for all invocations; or, perhaps, to use multiple dedicated stateful instances, which would allow you to easily pass values from themethod back to themethod (as discussed above).Thetypes provided by the support library also allow concurrent use of the same instance. The same does not hold for customclasses defined by extending the basic abstract class provided for this purpose.Among the many layers identified in the logical top-down structure of our workflow, some may not need any code, including catch and finally blocks, after the sublayer execution, that is:Converting such a layer in the Asynchronous Top-Down Chain format would produce highly redundant code: themethod would just call themethod, which would include all the stuff. One might be tempted to merge such a layer with the previous one, by expandinginline in the previous layer.This would be possible, provided that the previous layer does not involve an external API call; however, we suggest keeping the two layers and the related two Stages in all cases, so as to keep the separation of concerns identified at the logical level. To help with this task, the support library allows for a simpler and less redundant form:Note that we have to assign tothe same return type as the sub-Stage’s; in more general terms, the constraint onis the same as the Substage Constraint introduced above. As you may suspect, when ais used instead of a, themethod is executed immediately, in the forward chain (possibly through a), and nothing is added to the backward chain.There are other cases in which a strict application of the Asynchronous Top-Down Chain pattern is overkill and even less clear than the traditional use of callbacks.A typical one is shown below. Here, there is an, which must precede theblock and which is logically related with both theandblock. Suppose that this code cannot be made compliant with the Body Constraint. However, to simplify the scenario, theblock does not alter the variables it shares with theblock (though it may modify the values pointed to); moreover, theblock does not throw checked exceptions:Despite its simple form, this layer should still require splitting into two, in order to be converted. To relieve such cases, the support library allows for the resort to the classic callback-based syntax, which, in this case, is handier. This is done by simply generalizing the use of theintroduced before with. In practice, theblock can be transformed into a callback, which will be added to the backward chain, in this way:which allows the layer to fit into a. Note that, in this way, if any exception is pending after the execution of the forward chain, it is notified to thecallback as well.Calls tocan be placed freely withinin aand withinin a. They are not supported withinin aIn the above description of the Asynchronous Top-Down Chain pattern, the launch of a sublayer, possibly through a, was only performed from within theblock (hence, from inside themethod, for full Stages).However, as anticipated, there is no such limitation at all: the support library can handle invocations of a sub-Stage also from any part of themethod, including theandblocks, hence “resuming” the chain. In other words, the same code snippet supported in, that iscan be placed anywhere in, provided that a constraint very similar to the Substage Constraint is satisfied:The extra restriction posed forblocks can be stated more precisely in this way: nostatement is allowed in ablock, neither onnor after it. In fact, the execution ofmight cause an exception already pending to be overridden and this would happen after, hence violating the Resume Constraint.The conditions to satisfy the Resume Constraint depend on the place in whichis issued. Obviously, this call must be the last one in its block; to summarize the various cases:These restrictions mainly consist in suppressing some code parts. As before, they can be overcome by properly accommodating the logical structure and possibly spawning more layers.The further restriction on thecase (that an includedmust declare areturn type) is quite strange and needs a short discussion. It is just originated by a limitation in our support library’s ability to handle the case. It is certainly not an important restriction, as placinginstructions in ablock is discouraged in general, and ignoring the return value of a call is also not a recommended practice; still, it is error-prone, as the use of amethod may come unnoticed. Unfortunately, the case cannot be checked by the support library neither at compile time nor at runtime, and the consequent program behavior may not be as expected.To alleviate the hassle, the coder has the opportunity to put an explicit check on a Stage used in ablock, by invoking, which, in case of ainvoke, would throw aat runtime.One important point that has not been pointed out yet is that, because all processing done inpertains to the backward chain, the thread in which this processing will occur may not be easily predictable. In fact, the backward chain will be initiated in the thread on which the forward chain will be closed or will get an exception.So, if any heavy operation has to be made inafter the completion of the inner, and the thread allocation for the various tasks has to be kept under control, then it may be desirable to leverage the resumption support just introduced and to spawn a separate Stage for those operations, so as to redirect all the stuff in the appropriate thread pool.In the following example, we face a scenario that is still a fake one but that mimics typical situations that can sound familiar to many people involved in asynchronous programming.The code is supposed to do the following:Let's assume that reading, writing, and invoking the external service are all potentially blocking operations. Let's also assume that parsing the request is a very time consuming operation, which should be granted a limited thread pool.Finally, we want to keep socket management, request parsing, and servicing tasks well separated and encapsulate them into different classes.The logical structure of the problem is indeed simple and prone to a top-down representation; a blocking version of the code could be like this:where we lean on obvious definitions for the various base classes, exceptions, and external functions mentioned.The following could be the asynchronous version of the above code based on the Asynchronous Top-Down Chain pattern:The code is significantly longer and still leans on some more classes that have been omitted, that is the variousand), which take care of the asynchronous stuff by wrapping the base functions, in a way similar to the included. However, these classes can be defined in a general purpose way and, in, similar classes are included among the cited collateral library of reusable utility classes.On the other hand, this code entirely preserves the main structure of the blocking version, which helps identify the role of each piece.But the main benefit of this code style can be appreciated when a new exception is added to the declaration of any external method used in the processing: the exception will be outlined by the compiler and the development GUI will force the propagation of the exception declaration outward, until a suitable place for its management is reached, hopefully also assisting in the code modification work. This will be the same handy and safe process we are familiar with when dealing with synchronous code, which helps ensuring that all those annoying special cases that crop up when coding will be taken into account.In this article, we have introduced the Asynchronous Top-Down Chain pattern, a coding rule for the invocation of asynchronous methods that, by leaning on a proper support library, hides the use of callbacks.We have shown how, by sticking to this coding rule, a sequential logical flow can be expressed in Java in a synchronous style, fully taking advantage of Java exception handling, even if it includes invocations to asynchronous APIs.We have then analyzed in detail the benefits and the drawbacks of this approach and we have provided some sample code to see how the result may look like.An evaluation of whether or not obeying to the restrictions introduced helps in simplifying code writing and management is left to the reader.Thanks to Gianluca Finocchiaro (gianluca.finocchiaro@gmail.com) for reviewing the presentation and suggesting several improvements.Based on the initial feedback received, we changed the article title from "" to "".