VISITAS:

martes, 15 de abril de 2014

Ficheros de comandos de Windows

Los ficheros de comandos de Windows son una forma sencilla de automatizar algunas tareas.
Para crear un fichero de comandos, simplemente hay que ponerle la extensión .CMD
Veamos ahora algunas de las cosas que se pueden hacer en estos ficheros de comandos.

Comandos básicos

@echo off
Desactiva la salida de todos los comandos que se van ejecutando (incluido él mismo). Se suele poner como la primera línea de un fichero de comandos.

echo mensaje
Saca un mensaje en la consola.

msg * mensaje
Saca un mensaje en una ventana (popup), pero sin detener la ejecución.

pause
Detiene la ejecución de los comandos hasta que se pulse una tecla. Muestra el mensaje "Presione una tecla para continuar . . .". Si no se desea que salga el mensaje estándar, utilizar pause>nul (sin espacios).

exit
Termina la ejecución de los comandos.

rem
Línea de comentario

Comandos del sistema operativo

cd
Cambia a un directorio

copy
Copia un fichero a otro fichero o lugar

del
Borra un fichero

move
Cambia el nombre de un fichero o lo mueve a otro directorio

cls
Borra la pantalla

type
Muestra el contenido de un fichero

Creación de ficheros de texto

Es posible crear un fichero con contenido textual:
echo línea 1 > fichero.txt
echo línea 2 >> fichero.txt
echo línea 3 >> fichero.txt

Variables

set var=valor
Crea una variable y le asigna un valor

%var%
Accede al valor de la variable

set /P var=Introduzca valor:
Pide al usuario que introduzca el valor de la variable

set /A var=%var1% + %var2%
Realiza una operación matemática y el resultado lo mete en la variable

Redirección

|
Pasa la salida de un comando a la entrada de otro comando
Ejemplo:
     ipconfig | findstr /C:"LAN"

||
Ejecuta el segundo comando sólo si el primero falla
Ejemplo:
    copy fichero1 fichero2 || echo No existe el fichero

>
Escribe la salida de un comando en un fichero
Ejemplo:
    type fichero1 > fichero2

>>
Añade la salida de un comando a un fichero existente
Ejemplo:
    dir >> fichero2

<
Toma como entrada de un comando el contenido de un fichero

Condiciones

if [not] string1 == string2 comando
Ejecuta el comando si el string1 es igual al string2

if [not] exist fichero comando
Ejecuta el comando si existe el fichero

Saltos

goto etiqueta
.....
:etiqueta


jueves, 3 de abril de 2014

Ejecución de paquetes solaris

Cuando se instala un paquete Solaris con el comando pkgadd, lo que se hace es procesar el fichero prototype (man -s4 prototype).
Este fichero consta de líneas con uno de los siguientes formatos:


  • # comentario
  • ! comando
  • objeto

Las líneas de comentario se ignoran.
Las líneas de comando y de objeto se explican a continuación.

Líneas de comando

Se permiten los siguientes comandos:

!param=value

Permite definir variables que se pueden utilizar después.
Ejemplo:
        !USER=usuario2
        !GROUP=grupo4

!include

Para incluir otro fichero prototype

!default

Especifica el modo, usuario y grupo por defecto.
Ejemplo:
        !default 0755 $USER $GROUP

Líneas de objeto

En cada línea de objeto se especifica un fichero, directorio, etc.
El formato de las líneas de objeto es:

        type class location mode owner group

type

Especifica el tipo de objeto:
  • f : fichero
  • e : fichero para editar después de instalar
  • v : fichero volátil que puede cambiar después de la instalación. Se crea con tamaño cero (por ej, ficheros de log)
  • d : directorio
  • i : fichero de control (pkginfo, request, depend, postinstall, preremove, etc)

class

En pkginfo se definen una serie de clases. Por ejemplo:

    CLASSES="none parche"

En prototype se asigna una de estas clases a cada fichero. Estas clases indican el orden de instalación (en el ejemplo anterior, se instalarán primero los ficheros de la clase none y después los ficheros de la clase parche). En request se pueden decidir las clases a instalar.

location

Hay tres opciones para especificar un location:
  • path absoluto: el objeto se instala en dicho path absoluto
  • path relativo: el fichero es relocatable
  • path1=path2: el path2 indica un fichero cuyo contenido se copiará al path1 durante la instalación
Un path puede contener una variable:
  • $variable
  • si la variable comienza con minúscula, es una variable build time
  • si la variable comienza con mayúscula, es una variable install time (del request)

mode (opcional)

Son los permisos del objeto destino en octal

owner (opcional)

Es el usuario propietario del objeto

group (opcional)

Es el grupo propietario del objeto




viernes, 14 de febrero de 2014

Programación funcional en LUA

Las funciones como valores

En LUA, las funciones son valores con las mismas propiedades que el resto de valores (strings, números, tablas, etc). Las funciones se pueden almacenar en variables (globales o locales) y en tablas. Se pueden pasar funciones como argumentos y se pueden retornar funciones.
Una propiedad muy importante de las funciones en LUA es que tienen "lexical scoping". Esto significa que una función puede acceder a las variables de las funciones que le rodean. O sea, si una función f1 contiene internamente a otra función f2(), entonces todas las variables que se declaran en el scope de f1 pueden ser accedidas desde la función f2. Esto que parece algo sin importancia, se convertirá en LUA en algo fundamental para la programación funcional.
Las funciones en LUA son objetos anónimos, como el resto de valores, o sea, no tienen nombre. Cuando hablamos de la función print, realmente estamos hablando de una variable a la que se le ha asignado una función (valor) que muestra texto por consola.
Habitualmente, cuando escribimos una función, lo hacemos con la siguiente sintaxis:
function suma(a,b)
    return a + b;
end
Sin embargo, esto es una forma de escribir lo que realmente estamos haciendo, que es lo siguiente:
suma = function (a,b)
    return a + b;
end
Por consiguiente, una definición de función es realmente una sentencia de asignación que crea un valor de tipo "function" y lo asigna a  una variable.
Aunque lo normal es construir un valor "function" y asignarlo a una variable, hay ocasiones en que nos interesa construir la función y que permanezca anónima. Veamos un ejemplo.
La librería table ofrece una función table.sort() que recibe como argumento una tabla y ordena sus elementos. Esta función debe permitir ordenación ascendente, descendente, numérica, por fecha, etc de los elementos de la tabla. Para ello, en lugar de existir cientos de variantes de esta función, lo que se hace es pasar un segundo argumento (opcional) que es una función para comparar dos valores. Por ejemplo, supongamos la siguiente tabla de alumnos con sus notas:
alumnos = {
    { nombre="Luis", nota=6.3 },
    { nombre="Juan", nota=8.6 },
    { nombre="María", nota=9.8 }
};
Para ordenar esta tabla por nombre de alumno:
table.sort(alumnos, function (a1, a2) return a1.nombre < a2.nombre; end );
También podemos ordenar la tabla por nombre pero de forma descendente:
table.sort(alumnos, function (a1, a2) return a1.nombre > a2.nombre; end );
Las funciones que reciben como argumento otra función se denominan high-order functions.
Otro ejemplo de función high-order es el cálculo de la derivada de una función matemática en un punto. La derivada de una función f() en un punto es ( f(x+d) - f(x) ) / d
function derivada(f, d)
    d = d or 0.0001;
    return function (x)
        return (f(x+d) - f(x)) / d;
    end
end
De esta forma, podemos tener la función derivada de sen(x):
d_seno = derivada(math.sin);
Las funciones se pueden almacenar no sólo en variables globales, sino también en variables locales e incluso en tablas (esto dará lugar a los módulos y programación orientada a objetos en LUA).

Closures

Cuando escribimos una función dentro de otra función, la función interna tiene acceso a todas las variables de la función contenedora, incluidas las variables locales. A esta característica le llamamos lexical-scoping. Aunque esto suena bastante obvio, realmente no lo es, si lo combinamos con el hecho de que las funciones son valores como el resto de valores del lenguaje.
Veamos un ejemplo:
newCounter = function ()
    local i = 0;
    return function ()
        i = i + 1;    -- incrementa la variable local externa i
        return i;
    end
end
Esta función (que se asigna a la variable newCounter) crea y retorna una función anónima que incrementa el valor de la variable i (local a la función contenedora). ¿Qué ocurre cuando utilizamos newCounter para crear nuevas funciones? Cuando invoquemos a la nueva función construida, ¿cómo accederá a la variable i?
Veamos un ejemplo de uso:
func1 = newCounter();
print(func1());     -- 1
print(func1());     -- 2
Lo interesante en este ejemplo es que cuando invocamos a la función construida y retornada por newCounter(), se ejecuta una función que accede a una variable que era interna a su función contenedora, la variable i. Pero entonces, esa variable i, ya ha desaparecido ¿no? Ya que era una variable local a la función newCounter() y newCounter ya no existe. ¿Que ha ocurrido?
LUA maneja esta situación adecuadamente, mediante el concepto de closure. Para simplificar, un closure es una función más todo el entorno que la rodea. En este caso, la función retornada por newCounter() es un closure, que incluye la propia función anónima y la variable i.
Por ejemplo, si creamos otra nueva función anónima volviendo a invocar a newCounter(), se vuelve a retornar un closure nuevo con otra variable i:
func2 = newCounter();
print(func2());    -- 1
print(func1());    -- 3
print(func2());    -- 2
Esto ocurre porque func1 y func2 son dos closures distintos, cada uno con su propia instancia de la variable i.
Hablando técnicamente, los valores son closures, no las funciones.
Si la función newCounter() recibiera por ejemplo un argumento, ese argumento pasaría al closure:
newIncrementer = function (inc)
    local i = 0;
    return function ()
        i = i + inc;
        return i;
    end
end
En este caso, el closure incluye la función anónima, la variable i y el argumento inc.
Un ejemplo aclaratorio de los closures:
a = { };local x = 20;for i = 1, 10 do    local y = 0;    a[i] = function() y = y + 1; return x + y; end;end
En este ejemplo, el bucle for crea 10 closures (10 instancias de una función anónima). Cada uno de estos closures tiene una instancia de la variable y que es distinta. Sin embargo, todos los closures comparten la misma variable x.

Funciones en las tablas

Una consecuencia obvia de que las funciones (realmente closures) son valores como otro cualquiera es que podemos almacenarlas en variables locales, variables globales y en tablas.
Almacenar funciones en tablas es algo muy común en LUA. Por ejemplo, cuando llamamos a math.sin(2.56) estamos invocando a un miembro de la tabla math que se llama sin y es una función.
Para crear una tabla que tenga miembros función hacemos lo siguiente:
util = { };
util.suma = function (x, y) return x + y; end
util.resta = function (x, y) return x - y; end
De igual forma, también podemos utilizar el constructor de la tabla:
util = {
    suma = function (x, y) return x + y; end,
    resta = function (x, y) return x - y; end
};
Además, LUA ofrece una tercera alternativa para tablas con miembros función:
util = { };
function util.suma(x, y) return x + y; end
function util.resta(x, y) return x - y; end
En cualquiera de los casos, la invocación a las funciones de la tabla util sería de la siguiente forma:
util.suma(3, 2);
util.resta(4,7);

Funciones locales

Cuando almacenamos una función en una variable local, dichas funciones sólo pueden ser utilizadas en el scope en que se definen:
local func1 = function (a, b) return a ^ b; end
local func2 = function (x) return f(x, x); end
Esto es muy útil en los packages, donde las funciones del package (scope) pueden utilizar las otras funciones locales








martes, 11 de febrero de 2014

iOS: Certificados y perfiles de provisión

Introducción

Desarrollar programas para iOS (iPod, iPhone o iPad) tiene, además de lo que es el propio programa, otras tareas "accesorias" que a veces complican bastante el despliegue de la aplicación en dispositivos reales.
Apple adoptó la filosofía de que sólo Apple da permiso para que una aplicación corra en dispositivos iOS. Esto es algo bastante estricto, pero da muchas ventajas a los usuarios, ya que así se garantiza que las aplicaciones que se instalan han pasado por un control de Apple.

Certificados digitales de Apple

Pero, si sólo Apple puede autorizar aplicaciones, ¿cómo podemos desarrollar una aplicación nosotros?
La idea es que los desarrolladores tengan un certificado reconocido (autorizado) por Apple y que firmen las aplicaciones, de forma que los dispositivos vean que la firma está avalada por Apple.
Cuando un desarrollador se apunta al programa de desarrolladores de Apple, puede solicitar un certificado firmado por el propio Apple que se puede utilizar para firmar aplicaciones. Para ello, el desarrollador tiene que generar un CSR (certificate signing request) mediante la aplicación de llaveros de MAC OS.
La aplicación de llaveros genera automáticamente dos claves: una privada y una pública. El CSR lleva la clave pública, además del nombre y mail del desarrollador y se firma utilizando la clave pública. El CSR se envía a Apple (http://developer.apple.com). Como el CSR está firmado utilizando la clave privada, Apple se asegura que realmente viene del desarrollador.
Cuando Apple recibe el CSR, emite un certificado firmado por Apple. El certificado emitido por Apple contiene los mismos datos (incluida la clave pública) pero con la firma de Apple.
El certificado emitido por Apple (fichero .cer) lo descargamos a nuestro equipo y lo arrastramos (drag and drop) a la aplicación de llaveros.

Perfiles de provisión

Ya tenemos un certificado firmado por Apple con el que podremos firmar nuestras aplicaciones. Pero los dispositivos todavía no saben si pueden confiar en ti, y para esto surgen los perfiles de provisión.
Cuando creamos un perfil de provisión, lo que estamos haciendo es asociar una serie de dispositivos al certificado que hemos generado en el apartado anterior. El perfil de provisión es un fichero .provision se utiliza durante el proceso de compilación de una aplicación iOS y se despliega también en el dispositivo. Para crear un fichero de provisión hay que hacerlo en la web de desarrollo de Apple y descargarlo en el equipo. Luego se hace doble click y lo coge el Organizer de XCode.
Se pueden tener varios perfiles de provisión, uno para cada aplicación. Esto es lo normal.
En resumen, el perfil de provisión dice que el código compilado por nosotros se le permite ejecutar en una serie de dispositivos.

Compilación y ejecución de la aplicación

A la hora de compilar, lo importante es decirle a XCode que utilice nuestro certificado y el perfil de provisión.
Si miramos lo que contiene una aplicación compilada, podemos ver el perfil de provisión (que es una copia del original) y un directorio denominado _CodeSignature que contiene un fichero llamado CodeResources que es una lista de hashes de todos los ficheros del proyecto (realmente, firmas con nuestro certificado de todos los ficheros del proyecto).
Cuando se instala la aplicación, iOS hace una serie de comprobaciones: busca el fichero de provisión firmado por Apple, chequea los hashes en CodeResources utilizando la clave pública que viene en el certificado (asegurándose así que los ficheros no se han modificado durante el proceso).
Al ejecutar la aplicación, iOS vuelve a chequear el perfil de provisión y los hashes de todos los ficheros.

Para terminar...

Se necesitan dos certificados, uno para desarrollo y otro para distribución. Estos certificados se pueden utilizar para cualquier número de aplicaciones. Los certificados garantizan que la aplicación la ha desarrollado alguien reconocido por Apple, y además, garantizan que la aplicación no se ha modificado o corrompido.
El desarrollo de una aplicación para iOS tiene tres etapas:
  1. Desarrollo
  2. AdHoc
  3. Distribución
Cada fase tiene tres partes (aunque no todas las partes se aplican a todas las etapas):
  • Ficheros fuente
  • Certificados
  • Perfiles de provisión
Algunas de estas partes tienen elementos dentro de ellas:

  • Nombres de desarrolladores: son los que desarrollan la aplicación y la firman
  • UDIDs de dispositivos: son identificadores de dispositivos que se pueden utilizar para testear una aplicación (Apple permite un máximo de 100 dispositivos por desarrollador registrado).
  • AppIDs de aplicación



viernes, 24 de enero de 2014

LUA: Lenguaje de programación

Qué es LUA

LUA es un lenguaje de programación de extensión. Lo cual significa que no es un lenguaje para escribir programas ejecutables stand-alone. En lugar de esto, LUA está pensado para integrarse dentro de un programa host. Este programa host es el que se encarga de lanzar y ejecutar el programa LUA. Además, el programa host puede declarar funciones escritas en C que pueden ser invocadas desde el programa LUA.
LUA ofrece soporte para los siguientes conceptos de programación:

  • programación orientada a objetos
  • programación funcional
  • programación data-driven

El concepto de lenguaje de extensión es similar a VBA en las aplicaciones de microsoft office.
LUA tiene un garbage collector, con lo cual no es necesario liberar los objetos que se van creando. Pero hay que tener cuidado con referencias a objetos que se quedan para siempre en alguna variable.

Léxico

LUA tiene un léxico basado en C:
  • Los punto y coma al final de sentencia son opcionales.
  • Los identificadores constan de letras, números y underscore (no pueden empezar con números; tampoco se recomienda que empiecen con underscore, ya que esta notación se utiliza para identificadores internos de LUA)
Bloques: son una secuencia de sentencias que termina en end (funciones, bucles, if, bloques do -- end)
Palabras reservadas: and, break, do, else, elseif, end, false, for, function, goto, if, in, local, nil, not, or, repeat, return, then, true, until, while.
Operadores que son distintos a otros lenguajes:
  • ~= distinto
  • .. concatenación de strings
  • ... número variable de argumentos en una función
Comentarios: 
  • -- comentario de una línea
  • --[[ comentario multilínea ]]--

Variables, Valores y Tipos

LUA es un lenguaje tipado dinámicamente, lo que significa que las variables pueden apuntar a valores de cualquier tipo y, además, pueden apuntar a valores de diferentes tipos en diferentes momentos. Por ejemplo:
v = 12;
v = "abc";
Tipos de datos:

  • nil : indica que a una variable no se le ha asignado todavía ningún valor
  • boolean : puede ser true o false
  • number : todos los números son reales, punto flotante doble precisión
  • string : es una cadena de caracteres
  • function : un bloque de código con nombre
  • table : es similar a los objetos en otros lenguajes de programación
La función global type() devuelve el tipo de una variable: nil, boolean, number, string, function, table.
Una variable que se declara sin ámbito, es accesible desde cualquier lugar del programa (posterior a su declaración).
Si se desea una variable con ámbito de bloque (lo cual es muy recomendable), hay que declararla con ámbito local:
local v = 12038;
Asignación a múltiples variables:
local x, y = 1, 2; -- x = 1; y = 2;
 Declaración de una tabla:
local t = { "a", "b", "c" };
Acceso a los elementos de una tabla:
local a = t[1];  -- El primer índice de una tabla es 1 (no 0)
Operador longitud #:
local s = "test";
local t = { 1, 2, 3 };
print(#s);    -- 4
print(#t);     -- 3

Tablas

Simplificando, las tablas son objetos también, como los strings, números, etc.
Realmente, son arrays asociativos, esto es, arrays de elementos que se acceden utilizando un identificador. El identificador, también llamado clave, puede ser un índice numérico o un valor alfanumérico. Las tablas son similares a los diccionarios o tablas hash de otros lenguajes, y comparten características con los arrays. Las tablas son la estructura de datos más potente de LUA. De hecho, son la única estructura de datos que tiene LUA. Con una tabla podemos representar arrays, conjuntos, registros, listas, mapas, etc. También se pueden utilizar tablas para representar módulos y objetos (ej. io.read() es el método read() del módulo io, sin embargo para LUA, es la clave read de la tabla io).
La forma más simple de declarar una tabla vacía es:
local table = { };
Un array es una tabla:
local a = { "a", "b", "c" };
local v = a[2];   -- v = "b"; 
Una tabla un poco más complicada:
local t = { one = "a", two = "b", three = "c" };
En ambos casos se utilizan las llaves para definir los elementos de la tabla, pero en el segundo caso, se da una clave específica a cada uno de los elementos. Esta clave se puede utilizar para acceder a cada uno de los elementos de una de las siguientes maneras:
t["two"];
t.two;
Se pueden añadir elementos a una tabla después de creada:
local t = { };
t.property1 = "abc";
t["property2"] = "fer";
t[3] = "hola";
Ejemplo de creación de una tabla y cómo rellenar sus elementos:
local t = { };
for i = 1, 100 do t[i] = tostring(i) end;
Se está creando una tabla de 100 elementos donde cada elemento contiene un string.
Si se accede a un elemento que no existe, obtendremos nil:
print(t[200]);  -- nil
print(t["x"]);  -- nil 
print(t.x);  -- nil
Es importante distinguir entre t.x, t["x"] y t[x]. Los dos primeros casos son equivalente. El último depende del valor de la variable x.
Para eliminar un índice de una tabla simplemente hay que hacerlo nil:
local t = { a = 1, b = 2 };
t.a = nil;

Se puede utilizar el operador de longitud # para obtener el número de elementos que tiene una tabla.
Una tabla puede contener elementos de cualquier tipo (números, strings, funciones, o incluso otras tablas):
local tabla = {
    t = { prop1 = "hello" }
};
tabla.t.prop1;
Una tabla puede contener una función:
function f1()
end
local tabla = {
     fun1 = f1,
     fun2 = function() 
                end
}

Tablas como arrays

El equivalente LUA de un array es una tabla. Características de los arrays:

  • Todos los arrays en LUA tienen el índice base en 1 (no en 0 como en muchos lenguajes de programación). 
  • Los arrays necesitan tener un índice numérico. 
  • Los arrays pueden ser multidimensionales, haciendo que sus elementos sean a su vez arrays.
  • Los arrays no necesitan ser dimensionados en su declaración, ya que pueden crecer y dimensionarse en runtime.

Tablas como arrays asociativos (mapas)

El equivalente LUA para un array asociativo es una tabla. La diferencia entre un array asociativo y un array es que, en lugar de tener índices numéricos para acceder a los elementos del array, un array asociativo puede usar strings para acceder a sus elementos.
Se pueden mezclar en una misma tabla los dos tipos: array y array asociativo.

Tablas como objetos

Las tablas pueden usarse como objetos. Para ello podemos incluir funciones y datos en sus elementos.
Más adelante, en este mismo artículo, se verá la orientación a objetos en LUA.

Funciones

Una función en LUA se define con la palabra reservada function:
function add(n1, n2)
      return n1 + n2;
end
Nótese que los argumentos no tienen un tipo y por tanto se podrá pasar un valor de cualquier tipo a la función. Nótese también que no se especifica un tipo de valor retornado, y por tanto la función puede retornar cualquier valor (si no hay return dentro de la función, se retornará nil).
Una función puede retornar múltiples resultados:
function oper(n1, n2)
    return n1+n2, n1 - n2, n1 * n2;
end

Y se pueden recoger sus resultados en varias variables:
local x, y, z = oper(4, 6)  -- x = 10, y = -2, z = 24
 Una función puede aceptar un número variable de argumentos:
function fvar( ... )
    print(arg[1], arg[2], arg[3] );
end
 fvar(1, 3);  -- 1 3 nill
Algunos argumentos pueden ser fijos, y otros variables:
function fvar2( a1, a2, ... )
    print(a1, a2, arg[1], arg[2], arg[3] );
end
Como ya sabemos, se pueden añadir funciones a una tabla. Pero la función no tiene el concepto de self (this de otros lenguajes) y por tanto no podrá acceder a otros datos de la tabla. Para que la función sea realmente un método de la tabla la notación tiene que ser:
local table = { a = 3, b = 3 };
function table:add(n1, n2)
    return self.a + self.b + n1 + n2;
end
Y para invocarla:

 table:add(4, 7);

Funciones globales predefinidas

Realmente, en LUA una función global es una variable que tiene asignada una función.

 ipairs()

Permite recorrer los elementos de una tabla indexada numéricamente (no vale para tablas con identificadores).
for index, value in ipairs(table) do
end

pairs()

Permite recorrer los elementos de una tabla de cualquier tipo.
for index, value in ipairs(table) do
end

print()

 Imprime en consola todos sus argumentos separándolos por TAB.

tostring()

Convierte su argumento a string.
Lo interesante de este método es que cuando se aplica a una tabla que tiene definida una función en su propiedad __tostring, entonces llama a esa función.
Ejemplo:
local table = { a = "hola" };
function table:tostring() 
    return "a=" .. self.a; 
end
 setmetatable(table, { __tostring = table.tostring } );
print( table );   -- a=hola

type()

Retorna, como un string, el tipo de su argumento: Boolean, string, table, etc.

tonumber()

Convierte a número el parámetro de tipo string. Si el parámetro no representa un número, entonces retorna nil.

pcall()

Cuando ocurre un error en LUA, se propaga hacia arriba, normalmente hasta el programa host que lo maneja. En LUA no existe try/catch, pero existe pcall() que hace una función similar.
La llamada a pcall() incluye como parámetro la función que se quiere invocar y sus argumentos. Si ocurre un error dentro la función invocada, entonces pcall() retorna false y el error ocurrido. Si todo va bien, retorna true.
Ejemplo:
function badFunction(a, b)
    return a + b;
end
local result, error = pcall(badFunction,"1","2");

Estructuras de control

if

if condition1 then
    -- condition1 = true
elseif condition2 then
    -- condition2 = true
else
    -- all other cases

for

for variable = start, end, step do
    -- loop
end

while

while condition do
    -- loop
end

repeat

repeat
    -- loop
until condition;

Módulos

Cuando se escribe un programa LUA, se puede meter todo el código del programa en un único fichero main.lua, no hay problema sintácticamente hablando. Sin embargo, arquitecturalmente no es muy conveniente.
LUA ofrece el concepto de módulos para organizar mejor el código.
Desde el punto de vista de usuario, un módulo es un trozo de código LUA que puede ser cargado a través de require y que crea y retorna una tabla. Todo lo que el módulo exporta (funciones, constantes etc) se define dentro de esta tabla, la cual funciona como un namespace.
Por ejemplo, la librería estándar math es un módulo y se puede usar de la siguiente forma:
local m = require("math");
m.sin(2.4);
Sin embargo, el intérprete LUA precarga todas las librerías estándar con un código parecido al siguiente:
math = require("math");
string = require("string"); 
...
Con lo cual, podremos utilizar las librerías estándar como es habitual: math.sin(2.4);
Cada módulo sólo se carga una vez. Si más adelante, en otra parte del programa, se llama a require para volver a cargar un módulo ya cargado, LUA no lo carga, simplemente retorna la tabla que ya se ha cargado anteriormente. Si el módulo no se ha cargado anteriormente, require busca un fichero .lua con el nombre del módulo y lo carga.
La tabla package.loaded contiene todos los módulos cargados:
package.loaded.

La forma más sencilla de crear un módulo es la siguiente:

  1. creamos un fichero .lua con el nombre del módulo
  2. creamos una tabla local vacía
  3. ponemos en la tabla todas las funciones que queremos exportar
  4. retornamos la tabla

Ejemplo utils.lua:
local t_utils = { }; 
function utils.add(n1, n2) return n1 + n2; end; 
return t_utils;
Un ejemplo para manejar números complejos en LUA, fichero complex.lua:

local c = { }; 
-- Creación de un número complejo
function c.new (pr, pi) return { r = pr, i = pi }; end;
 
-- La constante i
c.i = c.new(0, 1);
 
-- Operaciones con números complejos
function c.add(c1, c2) return c.new(c1.r + c2.r, c1.i + c2.i); end;
function c.sub(c1, c2) return c.new(c1.r - c2.r, c1.i - c2.i); end; 
function c.mul(c1, c2) return c.new(c1.r * c2.r - c1.i * c2.i, c1.r * c2.i + c1.i * c2.r); end; 
local function inv(c) local n = c.r^2 + c.i^2; return c.new(c.r / n, -c.i / n); end; 
function c.div(c1, c2) return c.mul(c1, inv(c2)); end; 
function c.tostring(c) return "(" .. c.r .. "," .. c.i .. ")"; end; 
-- retornar la tabla
return c;
Utilización de este módulo:

local complex = requires("complex"); 
local c1 = complex.new(3,4); 
local c2 = complex.new(-3,6); 
local c3 = complex.div(c1, c2); 
print(complex.tostring(c3));

Metatablas

LUA tiene un conjunto de operaciones que son predecibles: podemos sumar dos números, concatenar dos strings, insertar una clave-valor en una tabla, etc. Sin embargo, no podemos sumar dos tablas o comparar dos funciones, por ejemplo. A menos que usemos metatablas.
Las metatablas nos permiten cambiar el comportamiento de un tipo de datos cuando se enfrenta a una operación no definida para ese tipo (recordemos que cada tabla es un tipo de datos distinto). Cuando intentamos sumar dos tablas, t1 + t2, LUA mira si alguna de ellas tiene metatabla y si la metatabla contiene la el miembro __add (que tiene que ser una función, llamado metamétodo en este caso). Si existe __add, LUA lo invoca para calcular la suma.
Cada tabla tiene su propia metatabla, pero los valores de otros tipos, números, strings, etc tienen una única metatabla para todos los valores de ese tipo.
Cuando se crea una nueva tabla, no tiene metatabla asociada. Podemos asociar una metatabla a una tabla mediante la siguiente función:
setmetatable(table, metatable);
En LUA sólo  podemos cambiar las metatablas de las tablas, no las metatablas de los demás tipos.

El metamétodo __tostring

Un uso típico de las metatablas es redefinir la función tostring() de una tabla. La función print(tabla) invoca a tostring(tabla), lo cual por defecto imprime algo que no es muy útil: 0x4724350. La función tostring() chequea si la tabla tiene una metatabla con la función __tostring. Así, para redefinir el comportamiento de tostring() para una tabla:
tabla =  { };
metatabla = { };
metatabla.__tostring = function(t)
    return ".....";
end
setmetatable(tabla, metatabla);

 El metamétodo __index

Como sabemos, si accedemos a un índice (o clave) de una tabla que no existe, el resultado es nil. Este es el comportamiento por defecto. Sin embargo, si la metatabla tiene un método denominado __index, en caso de no encontrar el índice, se invoca este metamétodo y se retorna su resultado.

Programación orientada a objetos en LUA

En LUA, una tabla es un objeto. Igual que los objetos, las tablas tienen estado. Como los objetos, las tablas tienen identidad, self.Y también, como los objetos, las tablas tienen un ciclo de vida.
Veamos otros aspectos de las tablas/objetos:

Operaciones

La siguiente definición crea una nueva función y la almacena en el campo retirar del objeto Account:
Account = { balance = 0 };
function Account.retirar(v) Account.balance = Account.balance -  v; end;
A esto casi se le podría llamar método de un objeto, pero no es exactamente, ya que utiliza dentro de la función a la variable global Account. Por tanto, sólo funcionará para ese objeto concreto.
Una primera solución es pasar como parámetro a la función el objeto sobre el que aplica:
function Account.retirar(self, v) self.balance = self.balance - v; end;
En los lenguajes orientados a objetos, self (o this) se suele ocultar en las llamadas y se pasa implícitamente. Esto también se puede hacer en LUA utilizando : en lugar de .
Account =  { balance = 0 }; 
function Account:retirar(v) self.balance = self.balance - v; end;
function Account:ingresar(v) self.balance = self.balance + v; end; 
a = Account;
a:ingresar(200); 
a:retirar(100);
El siguiente problema es cómo crear objetos del mismo tipo, o sea, cómo definir clases.
¿Cuál es la diferencia entre invocar a una función con . y con : ? Cuando usamos . le estamos diciendo a LUA que la función es un miembro de la tabla, del objeto tabla. Cuando usamos : le estamos diciendo a LUA que pase un parámetro oculto a la función que es el propio objeto, o sea, self.
Veamos un ejemplo sencillo:
a = { };
a.move1 = function (self, v)
    print("self = ", self, " v = ", v);
end
function a:move2(v)
    print("self = ", self, " v = ", v);
end
Ejemplos de uso:
a.move1(10);  -- self = 10 v = nil
a.move1(a,10);  -- self = 0x3457563 v = 10
a:move1(10);   -- self = 0x3457563 v = 10
a:move2(10);   -- self = 0x3457563 v = 10
a.move2(10);    -- self = 10 v = nil

Clases

Una clase es como un molde para crear objetos. Todos los lenguajes orientados a objetos ofrecen el concepto de clase. Pero LUA no. Sin embargo, es fácil emular este comportamiento. La idea es crear un objeto que sirva como prototipo para crear otros objetos:
Account = { balance = 0 }; 
function Account:new()
    obj = { };
    setmetatable(obj, self);
    self.__index = self;
    return obj;
end;
function Account:retirar(v)
    self.balance = self.balance - v;
end;
obj1 = Account:new();
obj2 = Account:new();
obj1:retirar(10);

















viernes, 6 de septiembre de 2013

Solaris packages

¿Qué son packages Solaris?

Conceptos y definiciones previas:
  • En Solaris, el software de aplicación se entrega en forma de packages.
  • Un package es una colección de ficheros y directorios.
  • El package lo construye el desarrollador del software.
  • Un producto software se distribuye en uno o más packages.
  • Los packages software los instala el administrador de la máquina.

Componentes de un paquete

Un paquete se compone de: 
  • Package objects: son los ficheros que componen la aplicación
  • Control files: controlan dónde, cómo y si el paquete se instala. Se clasifican en:
    • information files
    • installation scripts
Para empaquetar una aplicación se tienen que seguir los siguientes pasos:
  1. crear los ficheros necesarios: package objects (ficheros y directorios de la aplicación), ficheros prototype y pkginfo (obligatorios), otros ficheros opcionales, scripts de instalación (opcionales)
  2. construir el paquete con el comando pkgmk
El fichero pkginfo es un fichero obligatorio que define los parámetros del package: abreviatura del package, nombre completo, arquitectura, etc.
El fichero prototype es un fichero obligatorio que lista los componentes del package. Hay una entrada por cada package object, information file e installation script. Cada entrada consta de varios campos que describen el componente: localización, atributos y tipo de fichero.
El fichero compver es un fichero opcional que define versiones previas del package que son compatibles con la versión de este package.
El fichero depend es un fichero opcional que indica otros packages necesarios para instalar este package.
El fichero space define los requerimientos de espacio en disco para el package, además de los necesarios por los package objects.
El fichero copyright es un fichero opcional que define un mensaje de copyright que saldrá cuando se instale el package.
Los installation scripts no son obligatorios. Sirven para ejecutar acciones durante la instalación del package. Tienen las siguientes características:

  • Se componen de comandos bourne shell.
  • Los permisos del script deben ser 0644
  • El script no necesita el identificador de shell #!/bin/sh

Tipos de script de instalación:

  • request: pide información al usuario que está instalando el package
  • checkinstall: realiza verificaciones
  • procedure scripts: definen acciones que ocurren en puntos particulares de la instalación y desinstalación (preinstall, postinstall, preremove, postremove)
  • class action scripts: definen acciones a realizar sobre un grupo de objetos

Consideraciones antes de construir un package

Antes de construir un package hay que decidir si el producto consta de uno o de más packages. Cuando se decide hacer múltiples packages, hay que pensar en cómo segmentar la aplicación.
Consideraciones a tener en cuenta para decidir cómo segmentar la aplicación en packages:
  • todos los packages deben ser instalables remotamente
  • los packages deben instalarse en un file system concreto (/. /usr, etc) para soportar todas las configuraciones posibles de servidor.
  • los packages deben tener una funcionalidad claramente definida
  • los packages que requieran licencia deben ir separados
  • hay que evitar duplicidades y solapes en los ficheros/directorios de cada package

Comandos para construir packages

Crear un fichero prototipo que sirva de entrada a pkgmk: pkgproto
Crear un package instalable: pkgmk
Instalar un package: pkgadd
Almacenar respuestas a un request script: pkgask
Copiar packages en  un medio: pkgtrans
Desinstalar un package: pkgrm
Verificar la integridad de un package: pkgchk
Mostrar información sobre un package instalado: pkginfo
Mostrar los parámetros de un package instalado: pkgparam
Instalar un nuevo objeto en un package ya instalado: installf
Quitar un objeto de un package ya instalado: removef

Control Files

Package information files:
  • admin: administrative defaults file
  • compver: compatibility file
  • copyright: conpyright information file
  • depend: dependencies file
  • pkginfo: characteristics file
  • pkgmap: contents description file
  • prototype: information file
  • space: disk space requirements file
Optional installation scripts:
  • request: solicita información al usuario
  • checkinstall: chequea los requisitos de la instalación
  • preinstall: ejecuta acciones previas a la instalación
  • postinstall: ejecuta acciones posteriores a la instalación
  • preremove: ejecuta acciones previas a la desinstalación
  • postremove: ejecuta acciones posteriores a la desinstalación
  • class action: ejecuta acciones sobre un grupo específico de objetos

Construcción de un package

El proceso de construcción de un package

Los pasos típicos para la construcción de un package son (cuando se tiene experiencia se puede cambiar este orden):

  • Crear un fichero pkginfo para describir las características del package
  • Organizar el contenido del package en una estructura de directorios
  • Opcionalmente, crear ficheros de información: dependencias, copyright, de espacio en disco, etc.
  • Opcionalmente, crear scripts de instalación para customizar los procesos de instalación y desinstalación
  • Crear un fichero prototype
  • Construir el package con el comando pkgmk
  • Verificar el package comprobando que se instala bien
  • Distribuir el package

Variables de entorno

Las variables de entorno se pueden utilizar dentro de los ficheros pkginfo, prototype y en los ficheros de información.
Hay dos tipos de variables:
  • Variables de construcción: empiezan con una letra minúscula y se evalúan en tiempo de construcción del package (pkgmk)
  • Variables de instalación: empiezan con una letra mayúscula y se evalúan en tiempo de instalación (pkgadd)
Reglas generales sobre las variables de entorno:

  • En el fichero pkginfo, las definiciones de variables son de la forma PARAM=value, con la primera letra en mayúscula. Estas variables se evalúan sólo en tiempo de instalación (pkgadd)
  • En el fichero prototype, las definiciones de variables son de la forma !PARAM=value o $VAR=value, la primera letra puede ser mayúscula o minúscula. Sólo se evalúan las variables conocidas en tiempo de construcción

Fichero pkginfo

El  fichero pkginfo es un fichero ASCII que describe las características de un package junto a información que ayuda a controlar el flujo de la instalación.
Cada entrada en pkginfo es una línea que establece el valor de un parámetro de la forma:
PARAM = value
donde PARAM es uno de los parámetros estándar. El valor se puede poner entre comillas (simples o dobles) en caso de que se usen caracteres especiales de la shell.
Se pueden crear parámetros de usuario, los cuales deben empezar por mayúscula, indicando así que el parámetro se evaluará en tiempo de instalación.
Los siguientes parámetros son obligatorios en pkginfo:

  • PKG
  • NAME
  • ARCH
  • VERSION
  • CATEGORY

Además, pkgmk añade automáticamente los siguientes parámetros:

  • PATH
  • PKGINST
  • INSTDATE
Un mismo package puede tener diferentes versiones o ser compatible con distintas arquitecturas. Todas las variantes de un package se denominan package instances (determinadas por PKG, ARCH y VERSION).
El programa pkgadd asigna un identificador de package a cada instancia que se instala. Este identificador consiste en un nombre abreviado más un sufijo numérico. El identificador distingue una instancia de un package unívocamente en una máquina.

La abreviatura del package se define en el parámetro PKG de pkginfo. PKG tiene las siguientes características:

  • empieza por una letra y puede contener letras y números
  • el tamaño máximo es 32
  • habitualmente, los primero cuatro caracteres identifican a la compañía

El parámetro ARCH identifica la o las arquitecturas asociadas al package: ARCK=sparc
El parámetro VERSION identifica la versión del package. VERSION=release 1.0
El parámetro NAME especifica un nombre largo para el package (máximo 256 caracteres)
El parámetro CATEGORY especifica la categoría a la que pertenece el package: application o system.

Organización del contenido del package

Los objetos (ficheros) del package se organizan en una estructura de directorios que debería ser igual a la estructura que tendrán estos objetos en el sistema instalado. Esto ahorrará mucho tiempo y esfuerzo.
En primer lugar se crea un directorio para los objetos que componen el package (se recomienda que el nombre de este directorio coincida con el nombre abreviado PKG).
Después se organizan los objetos en una estructura de directorios igual a la del package ya instalado.

Fichero prototype

El fichero prototype contiene información sobre los objetos (ficheros, directorios) que componen el package.
Cada entrada de este fichero define un único objeto del package. Estas entradas tienen varios campos de información separados por espacios (en un orden especificado). Las líneas que empiezan por # se consideran comentarios y se ignoran.
Hay dos formas de crear un fichero prototype:
  • desde cero con un editor de texto
  • ayudado con el comando pkgproto
Los normal es utilizar la segunda opción para crear el fichero la primera vez y después editarlo con un editor de texto.
El comando pkgproto explora una estructura de directorios y ficheros, y genera un fichero pkgproto que luego se puede editar.

Formato de las entradas del fichero prototype:

  • part: (opcional) numérico, permite agrupar objetos en partes (por defecto, part=1)
  • ftype: un carácter, especifica el tipo de objeto
    • f = fichero ejecutable o de datos
    • e=fichero que se guarda previamente si existe
    • v=fichero volátil (ej. log)
    • d=directorio
    • x=directorio de acceso exclusivo a este package
    • l=link
    • p=named pipe
    • c=dispositivo de caracteres
    • b=dispositivo de bloques
    • i=fichero de información o script de instalación
    • s=link simbólico
  • class: es la clase de instalación para el objeto. Si no se especifica, se asume none.
  • path: path absoluto o relativo donde se instalará el objeto. Si el path es relativo, se usa el parámetro BASEDIR definido en el fichero pkginfo.
  • major: (opcional, utilizado en dispositivos)
  • minor: (opcional, utilizado en dispositivos)
  • mode: modo octal del objeto, por ej 0644 (permisos).
  • owner: propietario del objeto
  • group: grupo del objeto
Para crear un fichero prototype desde cero con pkgproto, cambiar al directorio anterior a donde se ha metido el contenido del package y ejecutar el comando pkgproto:

$ pkgproto ./pkg > ./pkg/package/prototype

Esto creará un fichero prototype con entradas como éstas:.

d none pkg/src 0755 jane staff
f none pkg/src/file.java 0555 jane staff
etc

Este fichero es útil para empezar, pero normalmente hay que editarlo y modificar algunas cosas. Como mínimo, hay que cambiar los ficheros de información (ftype=i):

i pkginfo=pkg/package/pkginfo
Además de definir cada objeto, en el fichero prototype se pueden hacer otras cosas:

  • definir objetos adicionales que se crean en la instalación
  • crear links durante la instalación
  • distribuir packages en múltiples volúmenes
  • anidar ficheros prototype
  • poner un valor por defecto para mode, owner y group
  • dar un path de búsqueda para pkgmk (en la construcción del package)
  • poner variables de entorno
Para crear un directorio (que no va incluido en el package file):
d none /directory 0644 root other
Para crear un fichero vacío:
f none filename=/dev/null 0644 bin bin
Para crear un link:
s none etc/mount=../usr/etc/mount
Para dar valores por defecto:
!default 0644 root other













lunes, 3 de junio de 2013

Eclipse: Aplicación Web con Tomcat y Web Service

1. Integrar Tomcat en Eclipse

1.1 Pasos previos

Descargar eclipse de eclipse (vale cualquier versión de eclipse 3.4 ganymede o superior)
Descargar Tomcat 6.0.37 de tomcat 

Descomprimir apache-tomcat-6.9.37.zip en un directorio [tomcat_home]
Descomprimir eclipse-jee-juno-SR2-win32.zip en un directorio [eclipse_home]

1.2 Crear un runtime de tomcat en Eclipse

Arrancar Eclipse
Window > Preferences > Server > Runtime Environments
Add...
    Apache > Apache Tomcat v6.0
Next
    Name: [tomcat_runtime]
    Installation Directory: [tomcat_home]
    JRE: el JRE que nos interese para ejecutar tomcat
Finish

2. Aplicación Web en Eclipse

2.1 Crear la aplicación

File > New > Other > Web > Dynamic Web Project
    Name: [project_name]
    Location: [project_location]
    Target runtime: [tomcat_runtime]
    Dynamic web module version: 2.5
    Configuration: Default configuration for Apache Tomcat 6
Next
    [app_name]
Next
Finish
Package Explorer > WebContent > New > JSP File
    index.jsp
Introducir algún contenido a la página JSP, por ejemplo:
    <%= new Date().toString() %>

2.2 Crear un servidor

Pestaña Servers
New server wizard
Apache > Tomcat v6.0 server
Host name: localhost
Server name: [server_name]
Server runtime: [tomcat_runtime]
Next
    Añadir el proyecto
Finish
Botón derecho sobre el server > Start
URL: http://localhost:8080/[app_name]/index.jsp

2.3 Cambiar el puerto de tomcat

Doble click sobre el server en la pestaña Servers
Editar los puertos
Salvar

3. Web Service

3.1 Pasos previos

Descargar Apache Axis 2 de axis 2
Descomprimir axis2-1.6.2-bin.zip en [axis2_home]

3.2 Configurar Axis2 en Eclipse

Abrir eclipse.
Window > Preferences > Web Services > Axis2 Preferences
En la pestaña Axis2 Runtime, definir el directorio de Axis: [axis_home]
OK

3.3 Crear el proyecto del Web Service

Eclipse:
File > New > Other... > Web > Dynamic Web Project
Next
Project Name: [svc_name]
Location: [svc_dir]
Target runtime: [tomcat_runtime]
Dynamic Web Module Version: 2.5
Configuration:
Pulsar el botón Modify (junto a la configuración custom)
Marcar:
  • Axis2 Web Services
  • Dynamic Web Module
  • Java
OK
Next
Next
En esta ventana podemos cambiar el [context_root] y el [content_dir]
Finish

3.4 Crear un servicio bottom-up

Un servicio bottom-up se crea definiendo la interfaz del servicio en Java y generando el WSDL a partir del interfaz Java. Por el contrario, un servicio top-down se crea definiendo el WSDL y generando las clases Java correspondientes.

Eclipse:
Package Explorer > [svc_name] > Java Resources > src > New > Package
[package_name]
Package Explorer > [svc_name] > Java Resources > src > [package_name] > New > Class
[class_name]
Definir operaciones dentro del servicio

Package Explorer > [svc_name] > Java Resources > src > [package_name] > [class_name].java
New > Other... > Web Services > Web Service
Next

Web Service type: Bottom up Java bean Web Service
Service implementation: [class_name]
En la escala del servidor poner: start service
En la escala del cliente poner: No client
Next

WSDL file: [wsdl_file]
Methods: Seleccionar los métodos que queremos que sean operaciones
Style and use: document/literal (wrapped)
Next

Pulsar el botón start server

Finish