viernes, 3 de junio de 2016

Scripting GX Java 8 Nashorn

A riesgo de parecer reiterativo, he querido hacer una actualización del tema de scripting Java, esta vez con GeneXus Xev3, Java 8 y el -no tan- nuevo engine Nashorn, que reemplaza al anterior Rhino.
Nashorn cuenta con algunas características que lo ponen por sobre su predecesor: mejor performance, posibilidad de invocar Java desde Javascript y viceversa, cuestiones de seguridad, etc..

El ejemplo que ahora les traigo es bastante sencillo y no pretende ahondar en cuestiones tan avanzadas, sino ser una guía inicial. De ahí en adelante depende de ustedes.
Por decir algo, alguien podría querer extender con Javascript las clases que genera GeneXus en un proyecto cualquiera...

Pero bueno, nuestro caso se trata de lo siguiente: tendremos un webpanel que llama a un procedure pasándole un pequeño script por parámetro. El procedure llamado es el que instancia el engine Nashorn y ejecuta el script.
Por su parte, el script, es una función que tiene su propia llamada a otro procedure gx, el cual hace una lectura a la tabla Product y retorna un string.

Webpanel testNashorn

El webpanel sólo cuenta con un textblock y un botón asociado al evento Enter, en el cual ponemos la llamada al procedure jsEngine, pasando el script a ejecutar.

Event Enter
 
 //el script a ejecutar: una función que invoca el método "generaMsg" y retorna un texto
 &script = 'function mainFunc() {' + newline()
 + '    var myMsg = jsEngine.generaMsg("hola");' + newline()
 + '    return myMsg;' + newline()
 + '}' + newline()
 textBlock1.Caption = jsEngine.Udp(&script)
 
EndEvent

Procedure jsEngine

En el procedure jsEngine se pueden distinguir dos secciones de código: la primera es donde usamos el truco para insertar un método público generaMsg a la clase java jsengine que GeneXus genera a partir del procedure. Aquí es donde ponemos un llamado a nuestro segundo procedure generaString.
En la segunda sección va el código de instanciar el engine Nashorn y ejecutar el script preparado.
//todas las variables son VarChar(1K)
parm(&script,&return);
//Sección1: iniciamos "cortando" el java que genera GX
java     privateExecute2();
java }
//para poder insertar un método "generaMsg", que luego invocaremos desde el javascript
java public String generaMsg(String msg) {
java     [!&tmpString!] = msg;
//acá podemos poner una llamada a otro procedure, en este caso al proc "generaString"
&tmpString = generaString.Udp(&tmpString)
java     return [!&tmpString!];
java }
java private void privateExecute2( ) {

//Sección2: instancia del engine y la invocación
java try {
java     javax.script.ScriptEngineManager m = new javax.script.ScriptEngineManager();
java     javax.script.ScriptEngine ngNashorn = m.getEngineByName("nashorn");
java     ngNashorn.put("jsEngine", this); //pasamos el objeto actual al engine
java     ngNashorn.eval([!&script!]); //hacemos eval del script
//invocamos la function "mainFunc" y manejamos el resultado
java     Object result = ((javax.script.Invocable)ngNashorn).invokeFunction("mainFunc");
java     [!&return!] = result.getClass().getName();
if &return = !"java.lang.String"
 java [!&return!] = result.toString();
endif
java } catch(javax.script.ScriptException exc) {
java     [!&return!] = "Error evaluating the script: " + exc.getMessage();
java } catch(NoSuchMethodException exc) {
java     [!&return!] = "Error evaluating the script: " + exc.getMessage();
java }
Por cierto que estas cosas se podrían implementar como un External Object y no hacer esta mezcla de código GeneXus y Java. Pero insisto en hacerlo de esta forma, porque así basta con pegar el código en un procedure y nada más, lo que resulta ser bastante práctico y sencillo, y favorece que el usuario gx se anime a probar sin mucha complicación.

Procedure generaString

En este proc, simplemente, concatenamos algunas cosas al string recibido y lo retornamos. Nótese que se debe crear una transaction con atributos ProductId y ProductName, y ponerle algún dato a la tabla.
//todas las variables son VarChar(1K)
parm(&inString,&outString);
for each
 &ProductName = ProductName
 exit
endfor
&outString = &inString + !", qué tal: " + &ProductName

Resultado

Con todo listo, sólo falta hacer Build y ejecutar el webpanel. Click al botón y ya está. Les dejo una vista del webpanel a modo de "evidencia".


Espero que esté lo suficiente claro el ejemplo, que lo corran y que se animen a hacer otras pruebas. Y, claro, a ir pensando cómo usar este tipo de cosas en sus proyectos de la vida real.
salu2!!!

3 comentarios:

  1. Hola, Creé un Slack para Genexus, abierto.
    A ver si a la comunidad le gusta, empieza a intercambiar experiencias en sus diferentes listas.
    Pasen el dato!

    https://genexus-group.slack.com/

    ResponderEliminar