Who is this article for?This article is intended for experienced ODI (Oracle Data Integrator) developers. Here are considered poorly documented aspects related to the order of execution of BeanShell-substitutions.
This is a continuation of
Part 1 .
After we have dealt with
each level of BeanShell-substitutions separately , let's see how these levels are consistent with each other when used together. Here we will talk only about the close cooperation of different and identical substitutions, when they literally penetrate into each other. How does their interpretation occur when they are nested in each other?
Impossibility of intersection of substitutions
This is the most obvious rule. It is mentioned here only for completeness. Here is an incorrect example:
<? <% ?> %>
This code will lead to an error. Substitutions can only be nested hierarchically, like elements in an XML document.
<? <% %> ?>
')
Earlier inside later
The easiest and most natural way to embed substitutions is to place the earlier ones inside the later ones. When executed, the early ones will be converted to text or will be removed (if the substitution code does not output anything to the output stream, then the result will be the removal of the substitution). Later substitution remains "clean", without artifacts that violate the syntax. For example, the code
<? String v_sess_info = " <%=odiRef.getSession("SESS_NO")%>; <%=odiRef.getSession("NNO")%>"; ?>
after the first pass will give something like this:
<? String v_sess_info = " 123123123555; 3"; ?>
Here, without performing string concatenation, the results of the odiRef methods are embedded in the text, which is later assigned to a variable. In the documentation, you can see that the SubstitutionAPI functions are the methods of the odiRef object, most often returning a String. However, nowhere will you find a direct indication that a particular function can be performed only at certain substitution levels. Moreover, the same function with one argument can work at one level, and with others - only at another. We will talk about this in one of the following articles.
So in a situation where, for example, at the β@β level, you need to get a value that SubstitutionAPI can return only with% -substitution, then nesting of substitutions is the only way to get the desired result.
Later inside earlier
If an interpreter performing, for example, a? -Substance suddenly encounters <@, it will stop execution by mistake. Naturally, he expects to see the Beanshell code here. That is, the reverse investment seems to be impossible ... But still possible.
If we hide the substitution code together with the metacharacters inside the text string and put this string into the stream, then the spent? Substitution will leave a later @ -substitution in the code. With the next passes, it will succeed. For example:
<? String fieldProcessing(String fldName, String srcTyp, String dstTyp){ ... return changedFldName; }?> ... <%=odiRef.getColList(0,"\t","<?=fieldProcessing("+'"'+"[COL_NAME]"+'"'+","+'"'+"[SOURCE_DT]"+'"'+","+'"'+"[DEST_DT]"+'"'+")?>","\n\t,","")%> ...
The odiRef.getColList function and others like it have many different mnemonics that allow you to refer to the names, types, format of fields, comments of fields entered in the data model, and other attributes. But the whole logic of code generation based on the list of fields is inside the SubstitutionAPI, and, it would seem, one can no longer intervene in it. And in rare cases, you may need:
- remove or add prefixes or infixes in field names;
- change the result of interpretation depending on the type or ratio of types of source and target;
- use the field header if it is entered in the model. But if not entered, use the field name (this can be useful, for example, to generate a header for a CSV file);
- provide special processing rules for the fields that are marked in the model by any attribute;
- and other virtuoso tasks.
To do this, you need an intermediate result when generating the code, which will produce the desired result the next time you perform the substitutions. The above example after the first pass will give something like
<? String fieldProcessing(String fldName, String srcTyp, String dstTyp){ ... return changedFldName; }?> ... <?=fieldProcessing("FIELD1","NUMBER","NUMBER")?>, <?=fieldProcessing("FIELD2","NUMBER","CHAR")?>, <?=fieldProcessing("FIELD3","CHAR","CHAR")?>, ...
If the embedded logic is simple, then it can be implemented right inside the line when calling odiRef.getColList. But the presentation of the code in the string is associated with a number of problems (even double quotes inside the string can lead to trouble, because \ u0022 does not always work because of the multi-pass interpretation). Therefore, it is better to bring this logic to the function, which is to be called.
Marking time
If one substitution can βprintβ another substitution, and the last one will work, then is it possible to do this while remaining at the same level of substitution? It turns out you can. There is reason to believe that some of the SubstitutionAPI functions themselves use this feature. For example, the odiRef.getOption function used at the β%β level can produce a result (option value), which also contains% -substitution. And they work great! This is the most obvious example. But provoking various kinds of errors can be noticed by logs, as at the levels β%β and β?β OdiRef-functions invisibly turn into snpRef, and sometimes this happens when β%β changes to β?β, But how many such iterations are performed, we donβt we know
Let's try to loop the interpreter:
<% Long n = 0L; String code1="/*<"+"%=n%"+">*/"; String code2="<"+"%"+"if(n++>10){out.print(code1);}else{out.print(code2);}"+"%"+">"; %> <%=code2%>
As long as n is not greater than 10, the same code is output. But he is also a% -permutation, and it all starts over. The iterative interpretation of the code ends when n = 11. Then the comment will be printed with the value n. The final code we will see in the ODI Operator is β/ * 12 * /β. If in this example instead of 10 we put 100, then the result of cyclic interpretation will be as follows:
### KEY com.sunopsis.res.gen / ODI-15015: Too much recursion in this text resolution.### (Command 0) out.print("\n") ; if(n++>100){out.print(code1);}else{out.print(code2);} ****** ORIGINAL TEXT ****** <%if(n++>100){out.print(code1);}else{out.print(code2);}%>
That is, the limit number of iterations is between 10 and 100. You can enter this code in the procedure by turning on the Ignore Errors option at this step, and in the next step output the value of the variable n (for example, / * <% = n%> * /) . Then we will know what the real depth of such recursive substitutions is. It is, surprisingly, not great.
By the way, please note that the exception that occurred during the execution of the% -stription did not stop the session, because the logs indirectly understand that the crash occurred later, during the final execution, which follows from the error "Token Parsing Error: Lexical error at line 1 , column 2. Encountered: "#" ".
The last example has no practical value, but gives the key to understand how the mechanism of substitutions is arranged.
In conclusion, it remains only to say that it is impossible to print a code containing an earlier substitution from later substitutions. That is, it can be done, but the substitution inserted in this way will remain as it is until the final execution and βbreaksβ the syntax is already there, because the corresponding interpreter has already been here, and with the sound of his hoofs, he moved to the next step of the session. He, like a pawn, walks only forward, and beats diagonally. And we also retire to write the next part, where we will examine the conditional interpretation and give examples of its use, including for uploading data into complex-structured files.