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 iteracción con el usuario final así como de estándares de comunicación entre aplicaciones, librerías y código re utilizable.
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 peticion.
- 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 utileria 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 graficos 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 causaria que estos se crearan con el usuario default (www-data) y esto seria 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 configuracion 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 tipicamente 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 generaran 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 seccion ”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 creo anteriormente para nuestro directorio publico 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 pagina 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 serializacion 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 peticion, cabe mencionar que al proceso de decodificar y regenerar el objeto una vez recibido por el programa se le llama deserializacion.
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 libreria 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.
#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.
map <string,string> Post; initializePost(Post); //notice that the variable are passed by reference!
Notese 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.
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.
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.
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;
Notese 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 calculo de la ecuación en el programa tradicional ocurre y no hay ninguna modificacion o transformación necesaria en este código, diciéndolo de otra manera, todo el código que hace las operaciones importantes de calculo, 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 utilizara 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.
std::string exec(char* cmd);
Para despues agregar la funcion que hace el trabajo de ejecutar el comando.
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.
// 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.
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.
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:
- Una cadena de error en caso de que haya ocurrido algún error en la ejecución
- Una cadena de mensaje adicional que envía la aplicación servidor al cliente
- El UUID único que se genero para el proceso de esta peticion independiente
- Un enlace al archivo de datos generado por la aplicación servidor
- 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.
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.
#!/usr/bin/perl -w
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");
#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 pagina que utilizara el usuario como punto de inicio para ejecutar programas, la pagina consiste en código HTML y código JavaScript que es este ultimo 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 pagina por cuestiones de simplicidad.
<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">
© 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 algun error en el proceso de programa de servidor es en esta sección donde se despliega dicho error.
A continuacion se ha agregado el contenedor de parametros y contiene cuatro cuadros de texto que permiten la introduccion de datos de parametros por parte del usuario seguido de una secciona 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 desplegara los enlaces a los archivos de datos asi como la imagen generada por gnuplot de la grafica de los datos del calculo.
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.
<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.
<script type="text/javascript">
$(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.
/* Clear data */
$("#btnClearData").click(function ()
{
$('#txtNa').val("");
$('#txtNb').val("");
$('#txtFa').val("");
$('#txtFb').val("");
$('#divStatus').empty();
$('#divImageResult').empty();
});
En el codigo que se muestra a continuacion suceden una gran variedad de cosas, una vez que se han introducido los datos para los parametros 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 parametros que seran enviados como peticion al programa servidor, a continuacion se define una funcion ”.ajax”de JQuery cuya funcion es hacer el llamado asincrono al programa servidor, en la funcion ”.ajax”se han definido algunas directivas como las siguietes.
beforeSend : Se ejecuta antes de enviar la peticion 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 exito. Tambien se puede ob- servar al inicio de la llamada AJAX que esta se configura con datos basicos 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 enviaran con la peticion al servidor, dataType: Tipo de datos que seran recibidos como resultado de los programas Servidor. El codigo 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.
/* 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 dinamicamente 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 asi 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 dinamicamente por JQuery a la objeto imagen de resultados para ser visible al usuario final.
Por ultimo 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.
/* 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, '');
}
});
</script>
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.
#!/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++ modicado y a continuacion 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 rapido el proceso de compilacion.
Una vez compilado el programa “Servidor”se copia a la carpeta CGI-BIN que se ha creado en la seccion de instalacion y conguracion de software, y una vez copiado habra que darle permisos de ejecucion para tener la capacidad de ejecutar el archivo desde el servidor web, para dar permisos de ejecucion se utiliza el siguiente comando.
$sudo chmod +x BlackBox.cgi
Una vez copiado a la carpeta cgi-bin y contando con permisos de ejecucion 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 ejecucion similares a los del programa cgi.
A continuacion procedemos a copiar el programa C liente”que basicamente consta de un archivo HTML al directorio “Document Root”que se ha creado en la seccion de Instalacion de Software”, una vez copiado el programa cliente podra ser accedido desde el servidor web.
Una vez terminados estos pasos la solucion sera accesible desde un navegador de internet y navegando a una URL en la forma de:
http://localhost/BlackBoxWeb.html
Lo que invocara al cliente y lo presentara en el navegador como pagina web.
Flujo Global de la Solucion
A continuacion se describe el ujo global de la solucion en linea y la manera en que esta funciona para brindar al cyber usuario una serie de resultados y gracas que se procesan del lado del servidor, esta serie de datos y gracas se producen de acuerdo a los parametros ingresados por el usuario y al calculo de una ecuacion que esta contenida en el programa servidor.
Basicamente la manera de como funciona la solucion 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 pagina 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 validos el programa envia una noticacion al usuario en el navegador pidiendo que los datos sean arreglados.
- En caso de que los datos sean validos, el programa cliente borra cualquier resultado previo si existiese para despues construir un objeto JSON con los datos que el usuario ha ingresado.
- El programa Cliente construye una nueva peticion al usuario y ejecuta el codigo que se encuentre en el evento “beforeSend”
- El programa Cliente envia los datos serializados y de manera asincrona al programa Servidor.
- En caso de originarse un error el programa Cliente ejecuta el codigo que se encuentre en el evento .error”.
- Si no hay errores en la peticion el programa Cliente ejecuta el codigo que se encuentre en el evento complete 2 queda a la espera de resultados por parte del programa Servidor.
- El programa Servidor es invocado y este verica que en la peticion se haya incluido un objeto JSON con los datos que el programa necesita para hacer el calculo.
- En caso de no tener los datos que el programa Servidor requiere, este aborta la operacion y construye y envia un objeto JSON que contiene la descripcion del error para que el programa Cliente lo despliegue en el navegador.
- En caso de que la peticion si 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 nalmente 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 identicador universal unico el cual utilizara para dar nombre a los archivos de datos y comandos que generara.
- El programa Servidor realiza los calculos matematicos originales que fueron programados en la aplicacion tradicional.
- El programa Servidor crea un archivo de datos con el nombre del identicador unico 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 identicador unico y con extension “.gnuplot”
- El programa Servidor escribe los datos basicos de los comandos que gnuplot debe ejecutar para generar la graca de este especico calculo, agregando como parametro el nombre del archivo “.dat”que acaba de generar y que contiene los resultados de los calculos.
- El programa Servidor guarda el archivo de comandos gnuplot en el directorio gnuplot.
- El programa crea un nuevo objeto JSON y le agrega informacion sobre mensajes al usuario, el identicador unico generado, un enlace creado dinamicamente que apunta al archivo de datos generado, un enlace creado dinamicamente que apunta al archivo de comandos gnuplot generado.
- El programa Servidor serializa el objeto JSON generado y lo envia de regreso al cliente, cabe mencionar que si sucede un error en el programa servidor en cualquier momento este genera un objeto JSON con el error y lo envia 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 verica 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 parametro el identicador unico que recibio del programa Servidor C++.
- El programa Servidor Perl es invocado y verica que la peticion contenga el identicador que necesita para poder gracar.
- En caso de que la peticion no contenga el identicador el programa Servidor Perl aborta la operacion.
- En caso de que la peticion sea correcta el programa Servidor Perl hace un llamado a gnuplot utilizando la consola y le pasa como parametros el archivo de comandos y el archivo de datos que el programa C++ genero.
- Gnuplot es invocado y ejecuta los comandos que el programa Servidor C++ ha generado y tambien utiliza los datos de los resultados que el programa Servidor C++ genero.
- Gnuplot genera una graca de los datos generados y la regresa como resultado al programa Servidor Perl.
- El programa Servidor Perl toma el resultado que Gnuplot genero y lo envia de regreso al programa Cliente.
- El programa Cliente recibe los resultados en forma de contenido y se los asigna dinamicamente al objeto imagen construido en HTML para desplegar los gracos.
- Los gracos se despliegan en el programa Cliente.
- Fin de la ejecucion.
Recomendaciones
Las recomendaciones basicas para cualquier usuario en cuanto a desarrollo se reere es que se contemple instalar entornos de desarrollo integrados IDE tanto para JavaScipt asi 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 mayoria son bastante recomentables.
Conclusiones
Se ha presentado una forma sencilla de transformar desarrollos tradicionales en soluciones en linea, se incluyeron simples deniciones y conceptos que se utilizan en todo el proceso de transformacion de un programa para que funcione en un ambiente web, tambien, se presentaron instrucciones y comandos para congurar un ambiente web y toda la conguracion que esto implica.
Se detallaron tecnicas, estandares y paradigmas que deberian ser utilizados y tener en cuenta al momento de realizar una transformacion de este tipo, se describio la forma de evitar problemas en soluciones en linea y las diferencias principales entre una solucion tradicional y una solucion web, se presento una forma novedosa y creativa de gracar sin dejar de utilizar el software tradicional para este tipo de cuestiones y se incluyeron los detalles de la transformacion en forma de codigo fuente y la manera en que se hace uso de una gran variedad de tecnologias que interactuan entre si de manera estandar para lograr un n comun.
Al nal el usuario puede distinguir claramente entre una aplicacion tradicional y una aplicacion web, asi como de todo lo que implica el exponer la aplicacion tradicional a un ambiente en linea, tambien, el usuario ahora entiende las enormes ventajas de tener una solucion en linea y la posibilidad de atender multiples clientes simultaneamente por un programa servidor.
Enjoy!




[...] Bueno creo que ya fue demasiada teoría aunque realmente la teoría nunca es suficiente, a continuación se presenta una implementación en web de la Suma de Riemann, para tener información mas amplia de como realizar un programa en web puedes visitar el post ¿Como portar una aplicacion existente a una solucion web? [...]