¿Como portar una aplicación existente a una solución web?

En el presente trabajo se plasma un tutorial detallado de como portar una aplicación existente al web y de como sacar ventaja de una gran variedad de tecnologías, paradigmas y técnicas tanto de interacción con el usuario final así como de estándares de comunicación entre aplicaciones, librerías y código reutilizable.

Introducción

La creciente demanda por soluciones accesibles y fáciles de utilizar desde internet ha motivado el desarrollo y la migración de cada vez mas sistemas a ambientes online, lo que implica un incremento en la complejidad de los desarrollos actuales e inversión de tiempo en la curva de aprendizaje y adopción de nuevas tecnologías.

Con esto en mente, se trata de plasmar una forma sencilla y directa en forma de tutorial paso a paso de como hacer esta transformación, tratando de disminuir en la medida de lo posible la complejidad agregada a los desarrollos así como el tiempo invertido para lograr dicha transformación.

Para la mejor organización del presente documento, se han establecido varias etapas donde se irán definiendo detalladamente y de forma incremental cada uno de los pasos para lograr la transformación de desarrollos actuales a soluciones en linea.

Requerimientos y Definiciones

Es bien sabido que en base a las tecnologías empleadas en los desarrollos actuales se eligen las tecnologías que se pueden utilizar en la transformación del desarrollo actual a solución en linea ya que por lo regular la plataforma y los lenguajes de programación involucrados en los desarrollos están directamente relacionados con las tecnologías web a utilizar para la transformación.

Requerimientos

A continuación se presenta una breve descripción de los requerimientos para la conversión, así como también se enlistan los paradigmas, técnicas y estándares a utilizar en todo el proceso.

  • Plataforma: Se ha establecido que la plataforma sobre la cual debe de ejecutarse la solución sea de tipo linux, este es un sistema operativo muy robusto y flexible que permite la utilización de una gran variedad de tecnologías tanto en linea como de forma local. Existe una gran diversidad de “sabores” o distribuciones en el mercado y las diferencias entre versiones destaca mayormente en cuanto a sistemas de resolución de dependencias, sistemas de instalación, paquetes disponibles y gustos, pero todos comparten el corazón de la plataforma, el kernel. Las instrucciones de instalación funcionan tanto para Ubuntu como para Debian que han sido las plataformas elegidas por su estabilidad y madurez.
  • Servidor Web: Para ejecutar una aplicación en linea se necesita un servidor web que se encargue de atender las peticiones generadas por parte de los usuarios y sea capaz de procesar y producir resultados visibles al código que realizo la petición, se ha elegido Apache como servidor web ya que es robusto, flexible y permite exponer una gran variedad de tecnologías en linea.
  • CGI: La tecnología de preferencia para dar soporte en linea a C++ y Fortran es CGI (Common Gateway Interface) protocolo de extensión que funciona en conjunto con el servidor web y permite la ejecución de código C++ y Fortran mediante peticiones HTTP de Apache, para utilizar  correctamente CGI se deben realizar algunas modificaciones al código fuente actual tanto para extraer datos de las peticiones web así como para retornar resultados al navegador o al código que hizo la petición.
  • Perl: Perl (Practical Extracting and Reporting Languaje) es un lenguaje de programación interpretado, dinámico y de alto nivel que se ejecuta del lado del servidor y hace que ciertas rutinas de programación sean mas fáciles de escribir, se ha utilizado dicho lenguaje para facilitar y simplificar ciertas rutinas que se detallan mas adelante.
  • gnuplot: gnuplot es una utilería portable orientada a la consola para graficar, utiliza una gran variedad de herramientas como lineas, puntos, cajas, contornos, vectores, superficies etc, para utilizar esta herramienta en una solución online se tendrá que hacer uso de técnicas personalizadas las cuales se enlistan mas adelante.

Instalación de software

En la presente sección se presentan los comandos de instalación y las librerías necesarias para crear el ambiente funcional para ejecutar aplicaciones web de forma local, se asume que se tiene una plataforma ya instalada de tipo linux para lo cual se recomienda instalar Ubuntu o Debian por ser flexible y robusto.

  • Compiladores y Herramientas: De inicio se procede a instalar los compiladores básicos para dar soporte a programas de Fortran y C++ utilizando la consola de linux, los comandos requeridos se enlistan en el orden antes mencionado.
$ sudo apt-get install gfortran
$ sudo apt-get install build-essential
  • Perl: Se procede a instalar Perl, que es uno de los lenguajes que se utilizan del lado del servidor para dar soporte de gráficos online, se instala mediante el siguiente comando.
$ sudo apt-get install perl
  • Servidor Web Apache: A continuación se procede a instalar el servidor web apache el cual se encarga de dar soporte a las peticiones de los programas clientes, se instala mediante el siguiente comando.
$ sudo apt-get install apache2

Una vez instalado, se debe reiniciar el servidor Apache para asegurarse de que se ha instalado correctamente.

$ /etc/init.d/apache2 restart

Una vez que se tiene Apache, se debe instalar el soporte para CGI y Perl con el siguiente comando

$ sudo apt-get install libapache2-mod-perl2

Y se reinicia Apache para que este reconozca los cambios agregados.

$ /etc/init.d/apache2 restart

A continuación, se procede a configurar el usuario con el que Apache se ejecuta, por default Apache se ejecuta con el usuario (www-data) pero se quiere cambiar este comportamiento ya que esta planeado crear archivos desde los programas CGI lo que causaría que estos se crearan con el usuario default (www-data) y esto sería un problema al momento de tratar de leer dichos archivos por cuestiones de permisos y seguridad, para cambiar el usuario default se tendrá que editar uno de los archivos de configuración de Apache con el siguiente comando.

$ sudo nano /etc/apache2/envvars

Donde nano es el programa de edición de preferencia, se puede usar gedit, vi etc. dependiendo de los gustos, el programa de edición muestra a continuación el archivo de configuración de Apache, una vez mostrado se procede a buscar el usuario con el que Apache se ejecuta, algo similar a lo siguiente.

export APACHE_RUN_USER=www-data
export APACHE_RUN_GROUP=www-data

Una vez identificado el usuario con el que Apache se ejecuta se procede a reemplazar el usuario por el usuario actual, es buena practica que en vez de reemplazar las lineas anteriores con el usuario nuevo sean creadas dos nuevas lineas y las anteriores simplemente sean comentadas, el resultado seria similar a lo siguiente.

#export APACHE_RUN_USER=www-data
#export APACHE_RUN_GROUP=www-data
export APACHE_RUN_USER=[usuario]
export APACHE_RUN_GROUP=[usuario]

Una vez teniendo hechos los cambios y guardado el archivo de configuración se procede a reiniciar el servidor web para asegurarse de que el servidor funciona correctamente con las modificaciones introducidas.

$ /etc/init.d/apache2 restart

A continuación y una vez configurado el usuario con que apache se ejecuta, se define una carpeta especializada la cual sera utilizada para albergar los archivos que contienen la lógica CGI así como los archivos que se ejecutan como programas cliente típicamente en un navegador, dicha carpeta se debe crear dentro de la carpeta del usuario actual y es necesaria para evitar problemas de permisos y seguridad al momento de estar creando y probando el código CGI modificado del desarrollo actual, también, dentro de la carpeta publica se debe crear un directorio el cual contendrá los archivos de datos y de comandos que se generarán automáticamente, la carpeta se llama gnuplot, las carpetas antes mencionadas se crean con los siguientes comandos.

$ sudo mkdir /home/[usuario]/public_html
$ sudo mkdir /home/[usuario]/public_html/cgi-bin
$ sudo mkdir /home/[usuario]/public_html/gnuplot

Donde (public_html) es un nombre opcional el cual es totalmente modificable, una vez que se tienen definidas y creadas las carpetas se procede a configurar nuevamente Apache para que reconozca dichas carpetas como contenedores de archivos públicos, para lograr esto, se debe editar otro archivo de configuración de Apache con el siguiente comando.

$ sudo nano /etc/apache2/sites-available/default

Con dicho comando se tendrá visible el archivo de configuración de Apache, y se procede a buscar el siguiente apartado en donde se encuentra la sección ”ScriptAlias” para modificar la ruta default por la nueva creada en pasos anteriores (cgi-bin), el apartado es similar al siguiente.

ScriptAlias /cgi-bin/ /var/www/cgi-bin/
<Directory "/var/www/cgi-bin/">
    AllowOverride None
    Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
    Order allow, deny
    Allow from all
</Directory>

Una vez modificada la ruta donde [usuario] es el usuario actual el resultado es similar a lo siguiente.

ScriptAlias /cgi-bin/ /home/[usuario]/public_html/cgi-bin/
<Directory "/home/[usuario]/public_html/cgi-bin/">
    AllowOverride None
    Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
    Order allow, deny
    Allow from all
</Directory>

El siguiente paso es agregar el soporte para que sean identificadas las tecnologías CGI y Perl en el directorio actual, por lo que se agrega .AddHandler cgi- script .cgi .pl justo debajo de las opciones para finalizar con algo similar a lo siguiente.

ScriptAlias /cgi-bin/ /home/[usuario]/public_html/cgi-bin/
<Directory "/home/[usuario]/public_html/cgi-bin/">
    AllowOverride None
    Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
    AddHandler cgi-script .cgi .pl
    Order allow, deny
    Allow from all
</Directory>

Una vez terminadas las modificaciones para la carpeta (cgi-bin) se procede a configurar el “Document Root” que es el directorio raiz que contendra los archivos públicos y para el cual se definió la ruta en pasos anteriores (public html), esta vez el apartado a modificar es similar a lo siguiente.

DocumentRoot /var/www/
<Directory/>
    Options FollowSymLinks
    AllowOverride None
</Directory>
<Directory /var/www/>
    Options Indexes FollowSymLinks MultiViews
    AllowOverride None
    Order allow, deny
    allow from all
</Directory>

Se modifica la ruta de tal forma que la nueva ruta apunte a la carpeta que se creó anteriormente para nuestro directorio público de archivos, al final se tendrá algo similar a lo siguiente.

DocumentRoot /home/[usuario]/public_html/
<Directory/>
    Options FollowSymLinks
    AllowOverride None
</Directory>
<Directory /home/[usuario]/public_html/>
    Options Indexes FollowSymLinks MultiViews
    AllowOverride None
    Order allow, deny
    allow from all
</Directory>

Una vez modificados y guardados los archivos de configuración, se reinicia Apache para verificar que los cambios introducidos a los archivos de configuración son correctos.

$ /etc/init.d/apache2 restart

gnuplot: Una vez finalizada la instalación y configuración de apache, se procede a instalar gnuplot como ultimo paquete necesario para terminar el ambiente funcional y ejecutar aplicaciones en linea localmente, para instalar gnuplot se debe descargar primero de la siguiente direccion  (http://www.gnuplot.info/) cabe mencionar que en caso de que se tenga instalado el paquete “Octave” no sera necesario instalar gnuplot ya que Octave lo instala por default, en caso de no tenerlo y una vez descargado y descomprimido, se ejecutan los siguientes comandos para instalarlo.

$ ./configure
$ make
$ make install

Estandares, Técnicas y Paradigmas

A continuación se enlistan los estándares, técnicas y paradigmas que se utilizan a lo largo del proceso de transformación de una aplicación tradicional a una solución en linea, se recomienda utilizar este tipo de practicas y librerías para que el software producido sea de alta calidad y lo mas estandarizado posible.

Paradigma Cliente-Servidor

El paradigma Cliente-Servidor consiste en que un programa Cliente realiza peticiones a un programa Servidor el cual realiza un trabajo y al final regresa un resultado al programa Cliente, en este caso el programa Cliente es la página web que contiene la solución en linea y el cual hace uso de JavaScipt para realizar peticiones al programa Servidor pudiendo también enviar parámetros y todo tipo de información para que el programa Servidor utilice en su proceso de calculo.

El programa Servidor en este caso se define como los programas C++ o Fortran modificados que se quieren tener en linea, este programa servidor es expuesto a Internet por medio de el servidor web y se encarga de atender las peticiones del programa cliente, cabe mencionar que en una solución típica el programa servidor atiende peticiones de muchos programas clientes y la capacidad de numero de clientes soportados depende del la infraestructura con que el programa servidor dispone.

JQuery

JQuery (http://jquery.com/) es una librería JavaScipt que simplifica la forma de manipular e interactuar con los objetos contenidos en HTML, tiene grandes ventajas y aplicaciones, se ha decidido utilizar esta librería para transformar de manera mas dinámica la iteraccion con los usuarios en las aplicaciones cliente que se ejecutan desde el navegador.

AJAX

AJAX (Asynchronous JavaScript and XML) Es un grupo de técnicas y métodos que se ejecutan del lado del cliente y que sirven para hacer llamados a código del lado del servidor de una manera asíncrona, usualmente este grupo de técnicas abarcan mas de una tecnología que en un escenario común se mezclan para lograr un objetivo, se ha decidido utilizar este grupo de técnicas utilizando la librería JQuery para hacer que los llamados a los programas CGI modificados sean de manera asíncrona y esto no impacte en otras partes del código cliente.

JSON

JSON (JavaScript Object Notation – http://www.json.org/) Es un formato ligero utilizado para intercambio de datos entre programas Cliente-Servidor el cual hace que los datos intercambiados entre programas sean fácilmente interpretados por personas, JSON es independiente de plataformas y muchos de los usuarios que lo utilizan lo consideran como un estándar para intercambio de datos entre diversas tecnologías y programas.

Serialización

Es el proceso mediante el cual se transforma un objeto de un programa para ser transmitido fácilmente por Internet o para ser almacenado en disco, en este caso la serialización se usa para codificar los objetos JSON de tal manera que estos puedan transmitirse por Internet en forma de peticiones al programa servidor el cual también utiliza esta técnica para devolver un resultado al programa cliente una vez que ha completado y procesado la petición, cabe mencionar que al proceso de decodificar y regenerar el objeto una vez recibido por el programa se le llama deserialización.

Getpost y Cajun

Getpost y Cajun son librerías que se utilizan en C++ para hacer que el desarrollo sea mas rápido y robusto, la funcion de Getpost es la de extraer fácilmente información o parámetros contenidos en las peticiones que son enviadas desde el programa cliente al programa servidor, dicha información permite la iteraccion dinámica entre programas, por otro lado, Cajun nos permite regenerar los objetos JSON y deserializarlos en C++, esta librería es necesaria ya que por decisión se ha utilizado el estandar de comunicación JSON y una de las maneras mas sencillas de cumplir con este requisito es utilizando esta sencilla librería.

UUID

UUID (Universally Unique Identifier) Es un identificador estándar definido por la OSF (Open Software Foundation) y consiste en la construcción de una llave única haciendo uso de una serie de reglas que aseguran que dicho identificador no se repita en todo el mundo, se hace uso de este estándar para definir identificadores únicos a lo largo de la transformación.

Transformaciones

Se inician las transformaciones en el programa tradicional en C++ con el que ya se cuenta, este programa se transformara de manera tal que recibirá, atenderá y procesara peticiones, las cuales contendrán datos, es decir, contendrán parámetros que el programa utilizara, este programa tradicional se convertirá en el programa “Servidor” deberá modificarse para recibir cuatro parámetros de entrada por parte del cliente, na, nb, fa y fb.

Programa Servidor

Se ha utilizado una librería sencilla llamada getpost.h la cual sirve de ayuda en el procesamiento de datos en las peticiones que recibirá el programa Servidor, también se ha utilizado Cajun como herramienta de regeneración y creación de objetos JSON. Para iniciar se agrega la cabecera de las librerías para poder utilizarlas en el desarrollo actual.

BlackBoxWeb.cpp

#include "getpost.h"
#include "json/reader.h"
#include "json/writer.h"
#include "json/elements.h"

A continuación se modifica el método main para utilizar Getpost, recuperar los datos de la petición y utilizarlos en variables locales.

BlackBoxWeb.cpp

map <string,string> Post;
initializePost(Post);   //notice that the variable are passed by reference!

Nótese que se utiliza el método InitializePost(Post); en donde Post es el tipo de petición que se estará esperando del lado del servidor, después, se establece que el resultado de nuestro desarrollo actual estará en formato JSON, es decir, que la nueva aplicación servidor utilizara un objeto JSON para responder a las peticiones.

BlackBoxWeb.cpp

cout << "content-type: application/json" << endl << endl;

Y finalmente se define la manera de extraer la información de la petición para utilizarla en el programa servidor utilizando el método “find” de la librería.

BlackBoxWeb.cpp

string userData;
string message = "";
if (Post.find("UserData") != Post.end()) {
    userData = Post["UserData"].c_str();
} else {
    ReturnError("Some requiered parameters were not provided, try again. <br />"); 
    return 1;
}

Con este código ya se cuenta con un objeto JSON que esta asignado a la variable userData y que proviene de la petición POST del programa cliente, el siguiente paso sera el de extraer y regenerar el objeto JSON utilizando Cajun como se muestra a continuación.

BlackBoxWeb.cpp

Object objDocument;
try {
    std::istringstream jsonData(userData);      
    Reader::Read(objDocument, jsonData);
 
    String jsonNa = objDocument["Na"];
    String jsonNb = objDocument["Nb"];
    String jsonFa = objDocument["Fa"];
    String jsonFb = objDocument["Fb"];
 
    double na, nb, fa, fb;
    na = atof(jsonNa.Value().c_str());  //na        = 2.3495;
    nb = atof(jsonNb.Value().c_str());  //nb        = 1.4595;
    fa = atof(jsonFa.Value().c_str());  //fa        = 0.5;
    fb = atof(jsonFb.Value().c_str());  //fb        = 0.5;

Nótese como utilizando el Reader de Cajun se lee el objeto y automáticamente se deserializa y se regenera en un objeto document de Cajun para después asignar ahora si a variables locales los valores enviados desde el cliente.

Una vez que se tienen las variables locales definidas con los valores que el cliente ha enviado, el proceso de cálculo de la ecuación en el programa tradicional ocurre y no hay ninguna modificación o transformación necesaria en este código, diciéndolo de otra manera, todo el código que hace las operaciones importantes de cálculo, resolución de ecuaciones u otro tipo de operacaciones complejas prácticamente NO se modifica a lo largo de la transformación por lo que el tiempo invertido de desarrollo de la funcionalidad clave de la aplicación permanece siendo el mismo.

Uno de los cambios no menos importante en cuestiones de transformaciones a soluciones en linea es la concurrencia, la concurrencia se refiere a múltiples procesos accediendo a un mismo recurso, pueden surgir problemas de concurrencia debido a que en aplicaciones en linea existen una cantidad indefinida de programas clientes accesando simultáneamente a un mismo programa servidor y este atendiendo peticiones de todos estos programas cliente, por lo que si no se tiene esto presente al momento de hacer la transformación podrían aparecer problemas como por ejemplo el tratar de escribir simultáneamente un mismo archivo al mismo tiempo, o el tratar de grabar al mismo tiempo en una columna de la misma fila de una tabla en una base de datos, entre otros.

En el caso de esta transformación se debe tener en cuenta que el programa tradicional genera datos los cuales son escritos a disco en forma de un archivo ASCII, el nombre del archivo siempre es el mismo actualmente por lo que existen riesgos de concurrencia conforme vaya aumentando el numero de clientes que accesan al programa servidor simultáneamente, para solucionar esto se necesita idear una forma creativa para prevenir riesgos de este tipo por lo que se opta por generar un nombre de archivo ÚNICO el cual cada petición independiente utilizará sin ningún riesgo de acceso por parte de otra petición, para concretar esta idea se hará uso del estándar UUID para generar identificadores únicos que serán utilizados como los nombres de los archivos de datos para mitigar el riesgo de concurrencia.

La técnica utilizada para hacer uso del estándar UUID es haciendo uso de la herramienta de linux llamada “uuidgen” que se encarga de construir y regresar como resultado un nuevo identificador UUID, para poder establecer comunicación con la consola de linux y hacer uso de esta valiosa herramienta en linea, habrá que utilizar un código especial que ejecute comandos de la consola de linux y recupere los resultados como se muestra a continuación.

Primero agregamos la definición a la función que se definió para ejecutar comandos de la siguiente forma.

BlackBoxWeb.cpp

std::string exec(char* cmd);

Para después agregar la función que hace el trabajo de ejecutar el comando.

BlackBoxWeb.cpp

std::string exec(char* cmd) {
    FILE* pipe = popen(cmd, "r");
    if (!pipe) 
        return "ERROR";
     
    char buffer[128];
    std::string result = "";
    while (!feof(pipe)) {
        if(fgets(buffer, 128, pipe) != NULL)
            result += buffer;
    }
     
    pclose(pipe);
    result.erase(std::remove(result.begin(), result.end(), '\n'), result.end()); // remove carriage return (new lines)
    return result;
}

Y para finalizar agregamos el código que llama a la función a para pedir un nuevo UUID y utilizarlo como nombre de archivo de datos.

BlackBoxWeb.cpp

// We must generate a unique ID for naming our data graph file
char uuidgen[] = "uuidgen";
string uuid = exec(uuidgen); 

Otra cosa que se ha agregado en la transformación es que se ha definido una carpeta especial que contendrá todos los archivos de datos generados por nuestro programa servidor, la carpeta de datos lleva por nombre “gnuplot” deberá estar ubicada en el ”Document Root”, a continuación se define el código para la ubicación de la carpeta y el nombre del archivo de datos generado.

BlackBoxWeb.cpp

string gpFolder = "../gnuplot/";
string gpDataFile =  uuid + ".dat";  

A continuación y debido a que se estara utilizando el programa gnuplot como graficador en linea, se debe de crear un archivo con los comandos que gnuplot ejecutara de la siguiente manera.

BlackBoxWeb.cpp

string gpCommandFile =  uuid + ".gnuplot";
ofstream gpCommands;
gpCommands.open((gpFolder + gpCommandFile).c_str());
if(!gpCommands) {
    ReturnError("** Failed to create the graph file. <br />");
    return 1;
}
 
gpCommands << "set terminal png small" << "\n";
gpCommands << "set title 'Graph by GnuPlot'" << "\n";
gpCommands << "set nokey" << "\n";
gpCommands << "plot '" << gpFolder << gpDataFile << "' u 3:1 w p" << "\n";
gpCommands.close();

Y para finalizar se debe construir un objeto JSON que sera enviado de regreso al código que realizo la petición para informar de los resultados del procesamiento del programa servidor, este objeto debe de contener por definición lo siguiente:

  1. Una cadena de error en caso de que haya ocurrido algún error en la ejecución
  2. Una cadena de mensaje adicional que envía la aplicación servidor al cliente
  3. El UUID único que se genero para el proceso de esta peticion independiente
  4. Un enlace al archivo de datos generado por la aplicación servidor
  5. Un enlace al archivo de comandos que gnuplot ejecuto en el proceso

Y el código de construcción del objeto JSON con los datos requeridos por definición se enlista a continuacion.

BlackBoxWeb.cpp

string linkToData = "<a href='/gnuplot/" + gpDataFile + "'>Download Data File</a><br />";
string linkToCommands = "<a href='/gnuplot/" + gpCommandFile + "'>Download Command File</a><br />";
message += "Calculations finished without errors. <br />";
 
// this is the script response
Object objResponse;
objResponse["Error"] = String("");
objResponse["Message"] = String(message.c_str());
objResponse["GraphID"] = String(uuid.c_str());
objResponse["LinkToData"] = String(linkToData.c_str());
objResponse["LinkToCommands"] = String(linkToCommands.c_str());
 
std::stringstream jsonStream;
Writer::Write(objResponse, jsonStream);
cout << jsonStream.str();  

A continuación y para terminar con los programas Servidor se enlista el programa sencillo hecho en Perl que se encarga de ejecutar los comandos generados de gnuplot utilizando para esto la consola y especificando el nombre del archivo de comandos a utilizar así como los datos a graficar para después recuperar de manera dinámica la gráfica generada por gnuplot y enviarla al programa Cliente el cual asignara de manera interactiva los datos a una imagen HTML para que la gráfica sea inmediatamente visible por el usuario final.

gnuplot.pl

#!/usr/bin/perl -w
 
# gnuplot_4.pl
# this is at least my fifth attempt.  Here I tried to call gnuplot in
# batch mode, where previous attmepts were in interactive mode.
# Additionally, the gnuplot commands are written to a temporary file
# and then gnuplot to retrieves them.
# To do: deal with gnuplot error messages, make commands more customizable,
# allow for plotting more than functions, links to gnuplot info.
 
use strict;
use CGI qw(:standard);
use CGI::Pretty qw( :html3 );
use CGI::Carp qw(fatalsToBrowser);
 
# define some variables
my ( $gp_file ) = ( param('gp_id') );
 
unless ($gp_file) { 
  die "ERROR: gp_file parameter required"; 
}
 
plot();
 
# call gnuplot 
sub plot {
   print header("image/PNG");
 
   # run in production code
   my $graph = `/home1/turegion/turegion/bin/gnuplot ../gnuplot/$gp_file.gnuplot`;
    
   # run local code
   #my $graph = `gnuplot ../gnuplot/$gp_file.gnuplot`; 
   
   print $graph;
}

En la próxima sección (Programa Cliente) se muestra la creación de un nuevo programa cliente, el cual consiste básicamente en una pagina web HTML utilizando JavaScript por medio de JQuery para hacer peticiones asíncronas a los programas de Servidor y de esa manera recuperar resultados en formato JSON, graficarlos dinamicamente y manejar los eventos que suceden de el lado del cliente.

Programa Cliente

A continuación se presenta el programa Cliente, que básicamente es la página que utilizará el usuario como punto de inicio para ejecutar programas, la página consiste en código HTML y código JavaScript que es este último el que se encarga de realizar las llamadas asíncronas a los programas Servidor, a continuación se muestra el código HTML personalizado de la pagina omitiendo la estructura general de una página por cuestiones de simplicidad.

BlackBoxWeb.html

<div id="divError" />
 
<div id="divDataContainer">
    <label>Na: </label>
    <input type='text' id='txtNa' maxlength="6" size="5" />
    <label>Nb: </label>
    <input type='text' id='txtNb' maxlength="6" size="5" />
    <label>Fa: </label>
    <input type='text' id='txtFa' maxlength="6" size="5" />
    <label>Fb: </label>
    <input type='text' id='txtFb' maxlength="6" size="5" />
</div>
 
<div id="divCopyright">
    &copy; 2011 Software developed by <a href="http://www.phoxonics.com">phoxonics</a>
</div>
 
<div id="divActions">
    <input type='button' value='Clear' id='btnClearData' />
    <input type='button' value='Process..' id='btnProcessData' />
</div>
 
<div id="divImageResult">  
   <table>
      <tr><td><div id="divLinks" /></td></tr>
      <tr><td><img id="imgResult" /></td></tr>
   </table>           
</div>
 
<div id="divStatus" />  

Como primera instancia se ha agregado un elemento de error, dicho elemento esta posicionado en la parte superior del documento personalizado y sirve como contenedor para mostrar errores, es decir, en caso de que suceda algún error en el proceso de programa de servidor es en esta sección donde se despliega dicho error.

A continuación se ha agregado el contenedor de parámetros y contiene cuatro cuadros de texto que permiten la introducción de datos de parámetros por parte del usuario seguido de una sección Copyright y de un contenedor de acciones, el cual contiene los botones de acciones a realizar sobre el presente programa.

Se prosigue con un contenedor de resultados el cual desplegará los enlaces a los archivos de datos así como la imagen generada por gnuplot de la gráfica de los datos del cálculo.

A continuación se agrega y describe paso a paso todo el código JavaScript que se encarga por medio de JQuery de los llamados asíncronos, primero se agrega una referencia a la librería de JQuery a utilizar, esta vez utilizamos una que ya esta en linea para no tener que agregar el código de la librería manualmente, se agrega con la siguiente linea de código, se estará utilizando JQuery version 1.5 que se encuentra alojado en los servidores de google.

BlackBoxWeb.html

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.min.js"></script>

A continuación se define el método principal que sera ejecutado al momento de que el documento esta cargado y sus elementos listos para ser accesados, el método se llama “.ready” es un método de JQuery.

BlackBoxWeb.html

$(document).ready(function() {   
    $.ajaxSetup({
          type: "POST",
          async: true,
          timeout: 5000,
          contentType: "application/x-www-form-urlencoded; charset=utf-8",        
          data: "{}"
        });     

Como se muestra en el código anterior, se ha definido un método de JQuery llamado “.ajaxSetup”, el cual se encarga de configurar las opciones que serán comunes entre todas las llamadas que se harán al servidor utilizando AJAX en este programa Cliente, se han definido algunos valores default los cuales pueden ser modificados dependiendo de las necesidades, como se puede ver se ha definido POST como el tipo de peticiones a utilizar.

Cabe mencionar que la mayoría de el código JavaScript se ha desarrollado utilizando la librería JQuery, a continuación se muestra la rutina para limpiar campos y resultados.

BlackBoxWeb.html

/* Clear data */
$("#btnClearData").click(function () { 
    $('#txtNa').val("");
    $('#txtNb').val("");
    $('#txtFa').val("");
    $('#txtFb').val("");
                     
    $('#divStatus').empty();
    $('#divImageResult').empty();
});

En el código que se muestra a continuacion suceden una gran variedad de cosas, una vez que se han introducido los datos para los parámetros na, nb, fa, fb, los datos se validan y en caso de ser validos se construye un objeto JSON que contiene todos los valores de los parámetros que serán enviados como petición al programa servidor, a continuación se define una función “.ajax” de JQuery cuya función es hacer el llamado asíncrono al programa servidor, en la función “.ajax” se han definido algunas directivas como las siguientes.

beforeSend : Se ejecuta antes de enviar la petición al servidor, complete: Se ejecuta al momento de que el llamado al servidor se complete, error : Se ejecuta solo en caso de que ocurran errores en la llamada al servidor, success: Se ejecuta cuando la llamada al servidor tuvo éxito. También se puede observar al inicio de la llamada AJAX que ésta se configura con datos básicos para poder realizar la conexion con el servidor, datos como type: tipo de peticion, url: direccion donde reside el programa servidor (vease: Compilando y Ejecutando Soluciones en Linea), contentType: tipo de contenido que reside en los datos que se enviarán con la petición al servidor, dataType: Tipo de datos que serán recibidos como resultado de los programas Servidor. El código dentro de los manejadores de eventos descritos anteriormente utiliza JQery para dependiendo del evento ocurrido sean los objetos HTML manipulados para borrar o mostrar resultados y graficas.

BlackBoxWeb.html

/* Process data */
$("#btnProcessData").click(function () { 
    if (!IsDataValid()) {
        return false;
    }
             
    var data = {
        'Na': $('#txtNa').val(), 
        'Nb': $('#txtNb').val(),
        'Fa': $('#txtFa').val(),
        'Fb': $('#txtFb').val()
    };
         
    $('#divStatus').empty();    
    $('#divStatus').append("<br />Started ajax request..<br />");
    $.ajax({
         type: "POST",
        url:  "cgi-bin/BlackBoxWeb.cgi",
        data: "UserData=" + JSON.stringify(data),               
          contentType: "application/json; charset=utf-8",           
        dataType: "json",            
        beforeSend: function (obj) {
            $('#divStatus').append("Before send..<br />");
        },
        complete: function (obj, status) {
            $('#divStatus').append("Completed..<br />");
            if (status == "success") {
                $('#divStatus').append("With success..<br />");               
            }
        },
        error: function (obj, status, obj2) {
            $('#divStatus').append("Error calling server: " + obj + "-" + status + "<br />");
        },
        success: function(data) {         
            if (data.Error != "") {
                $("#divError").css(color) = "red";
                $("#divError").text(data.Error);
                return false;
            }
                     
            $("#divStatus").append(data.Message);
            $("#divLinks").append(data.LinkToData);
            $("#divLinks").append(data.LinkToCommands);            
             
            var d = new Date();
            var cache = d.getTime(); 
            $('#imgResult').attr("src", "cgi-bin/gnuplot.pl?gp_id=" + data.GraphID + "&cache=" + cache);                        
        }
    });
});

Cabe mencionar que en la función success cuando el llamado al servidor es exitoso se recibe el objeto JSON que es enviado como resultado por el programa servidor y del cual se extrae la información y los resultados a mostrar, es también en esta parte donde se utiliza el programa de servidor Perl para ejecutar dinámicamente el llamado a gnuplot pasandole como parámetro el ID único de los datos que fueron generados y están guardados del lado del servidor en archivos con el nombre del ID, de esta forma, Perl llama a gnuplot enviándole tanto el archivo de comandos que gnuplot debe ejecutar así como el archivo de datos que fue resultado de los cálculos realizados en el programa servidor, como resultado se reciben los datos de una imagen los cuales son asignados dinámicamente por JQuery a la objeto imagen de resultados para ser visible al usuario final.

Por último pero no menos importante y para finalizar con la descripción de el programa cliente se tienen los métodos que se encargan de las validaciones los cuales se enlistan a continuación y no requieren de una explicación detallada ya que solamente son funciones JavaScript estandares para validar números decimales.

BlackBoxWeb.html

/* Checks if we have a valid matrix */
function IsDataValid() {
    var data = {
        'Na': $('#txtNa').val(),
        'Nb': $('#txtNb').val(),
        'Fa': $('#txtFa').val(),
        'Fb': $('#txtFb').val()
    };
 
    for (name in data) {
        if (ValidateDecimal(data[name]) == false) {
            alert("The value for " + name + ": " + data[name] + " is not valid, please fix it and try again.");
            return false;
        }       
    }
                     
    return true;
}
 
/* Validate decimal */
function ValidateDecimal(str) {
    str = alltrim(str);
    return /^[-+]?[0-9]+(\.[0-9]+)?$/.test(str);
}
 
/* all trim */
function alltrim(str) {
    return str.replace(/^\s+|\s+$/g, '');
}

Se ha recorrido un camino de tecnologías, técnicas, paradigmas, lenguajes y estándares y se ha definido con detalle el funcionamiento de cada una de estas entidades, en el siguiente apartado se describe de forma clara la manera en que todas estas tecnologías interactuan entre si para lograr un fin común y desplegar resultados tangibles para el usuario.

Compilando y Ejecutando Soluciones en Linea

En prototipos anteriores al BlackBox se ha creado un script personalizado en bash para la compilación y la creación automática de el programa servidor en forma de CGI, cabe mencionar que un programa CGI no es mas que el programa que contiene las modificaciones enlistadas en la sección “Programa Servidor” de C++ o Fortran compilado y con extensión CGI.

El archivo bash con el que se contaba también fue modificado y adaptado para esta versión del BlackBox el cual se incluye a continuación.

BlackBox.sh

#!/bin/bash
#
#  Compile the BLACKBOX solution.
#
 
# Compile C++ program
g++ BlackBoxWeb.cpp
if [ $? -ne 0 ]; then
  echo "Errors executing g++ -c BlackBox.cpp"
  exit
fi
 
# Rename the generated file to CGI
mv a.out BlackBoxWeb.cgi
 
# Clean logic
#rm BlackBoxWeb.o
 
echo "Ended script flow"
 
#  Terminate.
exit

El script anterior compila el programa en C++ modificado y a continuación lo renombra a CGI, como se puede apreciar es un proceso bastante sencillo que pudiera realizarse por medio de la consola pero se ha optado por un script para hacer mas rápido el proceso de compilación.

Una vez compilado el programa “Servidor” se copia a la carpeta CGI-BIN que se ha creado en la sección de instalación y configuración de software, y una vez copiado habra que darle permisos de ejecución para tener la capacidad de ejecutar el archivo desde el servidor web, para dar permisos de ejecución se utiliza el siguiente comando.

$ sudo chmod +x BlackBox.cgi

Una vez copiado a la carpeta cgi-bin y contando con permisos de ejecución el programa ya es accesible desde el servidor web. El programa de Perl debe ser copiado tambien al directorio cgi-bin para ser accesible por medio del servidor web, este no necesita compilarse ya que Perl es un lenguaje interpretado pero si se necesita darle los permisos de ejecución similares a los del programa cgi.

A continuación procedemos a copiar el programa Cliente que básicamente consta de un archivo HTML al directorio “Document Root” que se ha creado en la seccion de Instalación de Software, una vez copiado el programa cliente podra ser accedido desde el servidor web.

Una vez terminados estos pasos la solución será accesible desde un navegador de internet y navegando a una URL en la forma de:

http://localhost/BlackBoxWeb.html

Lo que invocará al cliente y lo presentará en el navegador como página web.

Flujo Global de la Solucion

A continuación se describe el flujo global de la solución en linea y la manera en que ésta funciona para brindar al cyber usuario una serie de resultados y gracias a que se procesan del lado del servidor, ésta serie de datos y gracias a que se producen de acuerdo a los parametros ingresados por el usuario y al cálculo de una ecuación que esta contenida en el programa servidor.

Básicamente la manera de como funciona la solución web es la siguiente:

  • El usuario teclea la URL en un navegador de internet.
  • El programa cliente es invocado por el servidor web y presentado al usuario en forma de una página web en el navegador.
  • El usuario ingresa valores para los parametros na, nb, fa, fb y presiona el boton procesar.
  • El programa cliente valida los datos.
  • En caso de que los datos no sean válidos el programa envia una notificación al usuario en el navegador pidiendo que los datos sean arreglados.
  • En caso de que los datos sean válidos, el programa cliente borra cualquier resultado previo si existiese para después construir un objeto JSON con los datos que el usuario ha ingresado.
  • El programa Cliente construye una nueva petición al usuario y ejecuta el código que se encuentre en el evento “beforeSend”
  • El programa Cliente envía los datos serializados y de manera asincrona al programa Servidor.
  • En caso de originarse un error el programa Cliente ejecuta el código que se encuentre en el evento “.error”.
  • Si no hay errores en la petición el programa Cliente ejecuta el código que se encuentre en el evento .complete y queda a la espera de resultados por parte del programa Servidor.
  • El programa Servidor es invocado y este verifica que en la petición se haya incluido un objeto JSON con los datos que el programa necesita para hacer el cálculo.
  • En caso de no tener los datos que el programa Servidor requiere, éste aborta la operación y construye y envia un objeto JSON que contiene la descripción del error para que el programa Cliente lo despliegue en el navegador.
  • En caso de que la petición sí cuente con los datos requeridos, el programa Servidor deserializa el objeto JSON y lo regenera en el ambiente C++.
  • Una vez regenerado el objeto JSON del lado del servidor el programa finalmente extrae los valores para na, nb, fa y fb y los valida para corroborar que son datos reales.
  • Una vez asignados los valores a variables locales el programa pide a la plataforma base un nuevo identificador universal único el cual utilizará para dar nombre a los archivos de datos y comandos que generara.
  • El programa Servidor realiza los cálculos matemáticos originales que fueron programados en la aplicacion tradicional.
  • El programa Servidor crea un archivo de datos con el nombre del identificador único y extension “.dat” donde escribe los resultados obtenidos.
  • El programa Servidor guarda el archivo de datos en el directorio gnuplot.
  • El programa Servidor crea otro archivo adicional con el nombre del identificador único y con extensión “.gnuplot”
  • El programa Servidor escribe los datos básicos de los comandos que gnuplot debe ejecutar para generar la gráfica de este específico cálculo, agregando como parámetro el nombre del archivo “.dat” que acaba de generar y que contiene los resultados de los cálculos.
  • El programa Servidor guarda el archivo de comandos gnuplot en el directorio gnuplot.
  • El programa crea un nuevo objeto JSON y le agrega información sobre mensajes al usuario, el identificador único generado, un enlace creado dinámicamente que apunta al archivo de datos generado, un enlace creado dinámicamente que apunta al archivo de comandos gnuplot generado.
  • El programa Servidor serializa el objeto JSON generado y lo envía de regreso al cliente, cabe mencionar que si sucede un error en el programa servidor en cualquier momento éste genera un objeto JSON con el error y lo envía al programa Cliente.
  • El programa Cliente recibe los resultados enviados por parte del programa Servidor en el evento “success”.
  • El programa Cliente deserializa y regenera el objeto JSON en el ambiente JavaScript.
  • El programa Cliente verifica si el objeto JSON de los resultados contiene mensajes de error.
  • En caso de contener errores el programa cliente los despliega en el objeto divError.
  • En caso de no contener errores el programa Cliente muestra los mensajes y los enlaces en los objetos creados para cada mensaje.
  • El programa cliente hace un llamado al programa Servidor Perl y le pasa como parámetro el identificador único que recibió del programa Servidor C++.
  • El programa Servidor Perl es invocado y verifica que la petición contenga el identificador que necesita para poder graficar.
  • En caso de que la petición no contenga el identificador el programa Servidor Perl aborta la operacion.
  • En caso de que la petición sea correcta el programa Servidor Perl hace un llamado a gnuplot utilizando la consola y le pasa como parámetros el archivo de comandos y el archivo de datos que el programa C++ generó.
  • Gnuplot es invocado y ejecuta los comandos que el programa Servidor C++ ha generado y también utiliza los datos de los resultados que el programa Servidor C++ generó.
  • Gnuplot genera una gráfica de los datos generados y la regresa como resultado al programa Servidor Perl.
  • El programa Servidor Perl toma el resultado que Gnuplot generó y lo envía de regreso al programa Cliente.
  • El programa Cliente recibe los resultados en forma de contenido y se los asigna dinámicamente al objeto imagen construido en HTML para desplegar los gráficos.
  • Los gráficos se despliegan en el programa Cliente.
  • Fin de la ejecución.

Recomendaciones

Las recomendaciones básicas para cualquier usuario en cuanto a desarrollo se refiere es que se contemple instalar entornos de desarrollo integrados IDE tanto para JavaScipt así como para C++, Perl, Fortran y cualquier otro lenguaje que se este utilizando. Actualmente existen muy buenos entornos de desarrollo para Linux como lo es: Eclipse, Netbeans, Oracle Studio, Aptana, Code::Blocks, BlueFish entre otros, ya es cuestion de gustos personales el utilizar uno u otro pero la mayoría son bastante recomentables.

Conclusiones

Se ha presentado una forma sencilla de transformar desarrollos tradicionales en soluciones en linea, se incluyeron simples definiciones y conceptos que se utilizan en todo el proceso de transformación de un programa para que funcione en un ambiente web, también, se presentaron instrucciones y comandos para configurar un ambiente web y toda la configuración que esto implica.

Se detallaron técnicas, estándares y paradigmas que deberían ser utilizados y tener en cuenta al momento de realizar una transformación de este tipo, se describió la forma de evitar problemas en soluciones en linea y las diferencias principales entre una solución tradicional y una solucion web, se presentó una forma novedosa y creativa de graficar sin dejar de utilizar el software tradicional para este tipo de cuestiones y se incluyeron los detalles de la transformación en forma de código fuente y la manera en que se hace uso de una gran variedad de tecnologías que interactuan entre sí de manera estándar para lograr un fin comun.

Al final, el usuario puede distinguir claramente entre una aplicación tradicional y una aplicación web, así como de todo lo que implica el exponer la aplicación tradicional a un ambiente en linea, también, el usuario ahora entiende las enormes ventajas de tener una solución en linea y la posibilidad de atender multiples clientes simultaneamente por un programa servidor.

¡Enjoy!

One thought on “¿Como portar una aplicación existente a una solución web?

Leave a Reply

Your email address will not be published. Required fields are marked *