Oracle Data Integrator SubstitutionAPI: Substitution Procedure. Part 2

  • Tutorial
Who is this article for?
This article is intended for experienced ODI (Oracle Data Integrator) developers. This section describes poorly documented aspects related to the order of execution of BeanShell substitutions.
This is a continuation of Part 1 .

After we deal with each level of BeanShell substitutions individually , we will see how these levels are consistent with each other when used together. Here we are talking only about close cooperation between different and identical substitutions, when they literally penetrate each other. How do they interpret when they are embedded in each other?

Impossibility of intersecting substitutions


This is the most obvious rule. It is mentioned here only for completeness. Here is an incorrect example:


   /* подстановка 2 конец */ %>

Such code will lead to an error. Substitutions can only be nested hierarchically, similar to elements in an XML document.


   /* подстановка 1 конец */ ?>


Earlier within later


The easiest and most natural way to nest substitutions is to place the earlier ones inside the later ones. When executed, the earlier ones turn into text or will be removed (if the substitution code does not output anything to the output stream, then the result of the execution will be the removal of the substitution). Later substitution remains "clean", without artifacts that violate the syntax. For example, code

; шаг <%=odiRef.getSession("NNO")%>";
?>

after the first pass will give something like this:


Here, without performing string concatenation, the results of odiRef methods are embedded in the text, which is later assigned to a variable. You can see in the documentation that the SubstitutionAPI functions are methods of the odiRef object that most often return String. However, nowhere can you find a direct indication that a particular function can be performed only at certain levels of substitution. 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 the SubstitutionAPI can only return using% substitution, then embedding substitutions is the only way to get the desired result.

Later inside earlier


If an interpreter performing, for example,? -Substitution suddenly encounters "<@", then it will stop execution by mistake. Naturally, he expects to see the BeanShell code here. That is, a reverse investment seems to be impossible ... But still it is possible.

If we hide the substitution code together with the metacharacters inside the text string and output this string to the stream, then the spent? -Substitution will leave a later @ -substitution in the code. In the next passes, it will succeed. For example:


...
<%=odiRef.getColList(0,"\t","","\n\t,","")%>
...

The odiRef.getColList function and the like have many different mnemonics that allow you to access the names, types, format of the fields, comments of the 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 that you 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 the interpretation depending on the type or correlation of the types of source and target;
  • use the field title if it is entered in the model. But if not entered, then use the field name (this can be useful, for example, to generate the header of a CSV file);
  • provide special processing rules for the fields marked in the model with some feature;
  • and other virtuoso tasks.

For this, it is necessary that when generating the code, an intermediate result is obtained, which, when the next substitution is performed, will give the desired result. The above example after the first pass will give something like


...
	,
	,
	,
...

If the embeddable logic is simple, then it can be implemented right inside the line by calling odiRef.getColList. But presenting the code in a string is fraught with a number of problems (even double quotes inside a string can lead to troubles, because \ u0022 does not always work due to a multi-pass interpretation). Therefore, it is better to put this logic into a function that you call.

Stamping on the spot


If one substitution can "print" another substitution, and the latter will work, can this be done while remaining at the same level of substitutions? It turns out you can. There is reason to believe that some of the functions of the SubstitutionAPI themselves take this opportunity. For example, the odiRef.getOption function, used at the "%" level, can give a result (option value), which also contains% substitutions. And they work great! This is the most obvious example. But provoking various kinds of errors, one can notice from the logs how at the levels “%” and “?” The odiRef functions invisibly turn into snpRef, and sometimes this happens when the “%” switches to “?”, But how many such iterations are performed, we don’t 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%>

Until n is greater than 10, the output of the same code occurs. But it is also a% substitution, and it all starts all over again. An iterative interpretation of the code ends with n = 11. Then a comment with the value n will be printed. The final code that we will see in the ODI Operator is “/ * 12 * /”. If in this example, instead of 10, put 100, then the result of a cyclic interpretation will be like this:

### 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 maximum number of iterations is between 10 and 100. You can enter this code in the procedure by enabling the Ignore Errors option at this step, and in the next step display the value of the variable n (for example, like this: / * <% = n%> * /) . Then we will find out what the real depth of such recursive permutations is. She is, surprisingly, not great.

By the way, note that the exception that occurred while executing the% substitution did not stop the session, because the logs indirectly make it clear that the crash occurred later, during the final execution, which follows from the Token Parsing Error: Lexical error at line 1 , column 2. Encountered: "#" ».

The last example has no practical value, but gives a key to understand how the permutation mechanism works.

In conclusion, it remains only to say that it is impossible to print code containing earlier substitution from later substitutions. That is, it is possible to do this, but the substitution inserted in this way will remain as it is until the final execution and will “break” the syntax already there, because the corresponding interpreter has already been there, and soberly tapping the hooves has proceeded to the next step of the session. He, like a pawn, moves only forward and hits diagonally. And we also retire to write the next part, where the conditional interpretation will be considered and examples of its use will be given, including for uploading data to complexly structured files.

Also popular now: