TALF Hoja 4 (16 de Marzo de 2010)

P1: Convierte el AFND de la hoja anterior en un autómata finito determinista. Incluye en tu solución la tabla de conversión tal como lo vimos en clase, la quintupla del AFD obtenido finalmente, y su grafo.

Partimos del siguiente AFND.

AFND BaseAñadimos el estado de Error.

AFND Base con estado de error

Comenzamos a crear la tabla de conversión

partimos del estado inicial {a}. con 0 vamos a {a} y con 1 a {a,b}

Tabla AFND AFD paso 1{a} ya pertenece al conjunto de estados, pero {a,b} no por lo tanto añadimos el estado {a,b}.

Tabla AFND AFD paso 2en el conjunto {a,b} con valor 0  a nos lleva a {a} y b nos lleva a {c,d} por lo tanto {a,b} con 0 nos lleva a {a,c,d} como {a,c,d} no esta en el conjunto de estados lo añadimos y añadimos las transiciones.

Tabla AFND AFD paso 3repetimos el procedimiento hasta que no se creen mas elementos para el conjunto de estados. quedándonos la siguiente tabla (a menos que me haya equivocado)

Tabla AFND AFD paso final

ahora pasamos la tabla a un grafo.

AFD sin estados finales ni inicialesLos primeros estados iniciales de nuestro AFND “con los que empezamos la tabla” son nuestros estados iniciales, en esta caso A.

AFD sin estados finales pero con inicialesTodo estado del AFD donde exista un estado final del AFND es final (en este caso todos donde este d)

AFD convertidoFinalmente la quintupla queda definida por:
M=(∑,Q,δ,q0,F)

siendo

  • ∑   El alfabeto
  • Q   Conjunto finito de estados
  • δ    la función de transición
  • q0 conjunto de estados iniciales
  • F    conjunto de estados finales
por lo tanto para este AFD la quintupla sera:
  • ∑   {0,1}
  • Q    [{a},{a,b},{a,c,d},{a,b,c},{a,d,error},{a,b,d,error},{a,b,c,d},{a,error},{a,b,error},{a,c,d,error},{a,b,c,error},{a,b,c,d,error}]
  • δ    es la tabla de transiciones
  • q0 {a}
  • F     [{a,c,d},{a,d,error},{a,b,d,error},{a,b,c,d},{a,c,d,error},{a,b,c,d,error}]

Informática Gráfica Interactividad

En esta parte de la práctica aprenderás a utilizar las posibilidades que ofrece la biblioteca funciones de utilidades glut de OpenGL para dotar a tu aplicación de interacción con el usuario. Estudia como maneja los eventos producidos por la pulsación de los botones del ratón o de las teclas en el teclado alfanumérico.

La tarea interactiva más simple que implementa la librería glut es permitir que se controle la ejecución de la aplicación mediante un bucle principal de visualización. El formato de la orden es:

void glutMainLoop (void);

La ejecución de esta función espera a que se produzcan eventos. Cada vez que se produce un evento lo atiende y ejecuta la función registrada, encargada de responder al evento (función de callback).

Los posibles eventos que puede responder OpenGL se dividen en tres grupos:

Eventos de ventana.

Se generan al crear la ventana de la aplicación, cambiar su tamaño o su posición en la pantalla. Los eventos de ventana se atienden registrando una función de respuesta con la función:

void glutReshapeFunc (void (*func)(int ancho, int alto));

Eventos de teclado.

Se generan al pulsar alguna tecla. Se atienden definiendo una función de respuesta con la función:

void glutKeyboardFunc (void (*func)(unsigned char key, int x, int y));

Eventos de ratón.

Para registrar la función de respuesta cuando se produce una pulsación de algún botón del ratón se usa:

void glutMouseFunc (void (*func) (int boton, int estado, int x, int y);

Para registrar la función de respuesta cuando el ratón se está moviendo sobre la ventana activa se usa;

void glutMotionFunc (void (*func)(int x, int y));

En esta práctica vamos a trabajar con los eventos de teclado y ratón, dejando los de ventana para prácticas posteriores.

1. Crea un proyecto nuevo y añade los ficheros: raton.c y raton.h. En primer lugar examina el código y pregunta a tu profesor de prácticas cualquier cosa que no entiendas. Compila y ejecuta el código. Comprueba el funcionamiento del programa presionando el botón izquierdo del ratón y las teclas r , g y Esc.

2. Modifica el código para que además atienda a los siguientes eventos de teclado:

Tecla ‘b’ fija el color a azul
Tecla ‘y’ fija el color a amarillo
Tecla ‘R’ fija el color de fondo a rojo
Tecla ‘G’ fija el color de fondo a verde
Tecla ‘B’ fija el color de fondo a azul
Tecla ‘Y’ fija el color de fondo a amarillo

OpenGL trabaja con el espacio de colores llamado RGB (Red Green Blue, o bién rojo verde azul). Así lo especificamos al haber llamado a glutInitDisplayMode(). Cada color está definido por tres valores entre 0 y 1, ambos incluídos. La siguiente tabla contiene las definiciones de los colores mas llamativos:

color     componente(red)     componente(green)     componente(blue)
rojo         1.0             0.0             0.0
verde         0.0             1.0             0.0
azul         0.0             0.0             1.0
blanco         1.0             1.0             1.0
negro         0.0             0.0             0.0
amarillo     1.0             1.0             0.0
magenta     1.0             0.0             1.0
cyan         0.0             1.0             1.0

3. Modifica el código para que al pulsar el botón derecho del ratón se borre la ventana.

4. Modifica de nuevo el código de forma que al pulsar el botón izquierdo del ratón se capture el primer punto de la recta, y al soltarlo se capture el segundo y se dibuje la recta que une ambos puntos.

5. Modifica el código para que antes de soltar el botón izquierdo siempre sea visible la recta que se dibujaría.

6. Modifica el código de forma que los dos puntos indicados por el usuario sean la diagonal de un rectángulo y lo visualice.

7. Añade un evento de teclado que te permita seleccionar entre dibujar líneas y rectángulos.

libreria .h

#ifndef OCUADRADO_H
#define OCUADRADO_H

/* Variables Globales del modulo */
int VentanaAncho = 500, VentanaAlto = 500; /* Tamanyo de la ventana */
int VentanaX = 100, VentanaY = 100; /* Posicion de la ventana */
int Pulsaciones; /* Numero de pulsaciones del boton izquierdo del raton */
int InicioX, InicioY; /* Coordenadas del punto inicial */
int FinX, FinY; /* Coordenadas del punto final */
float cpR,cpG,cpB;/*Color Pintado red color Pintado Green and Color Pintado Blue*/
float cfR,cfG,cfB;/*Colores de fondo*/
int modoLinea;//Booleando en true dibuja lineas en falso dibuja rectangulos o cuadrados

/* Abre una ventana OpenGL */
void AbreVentana (int numeroArgumentos, char ** listaArgumentos);

/* Funcion de dibujado */
void Dibuja(void);

/* Establece el area visible */
void TamanyoVentana (int alto, int ancho);

/* Inicia las funciones de Callback */
void IniciaFuncionesCallback (void);

/* Define las acciones tras una pulsacion del teclado */
void TecladoCallback (unsigned char tecla, int x, int y);

/* Funcion de callback de las acciones del raton */
void RatonCallback (int boton, int estado, int x, int y);

#endif

el archivo c

#include <GL/freeglut.h>
#include <stdio.h>
#include "main.h"

#ifndef FALSE
#define FALSE (0)
#define TRUE (!(FALSE))
#endif

/******************************************************************************************/
/* Funcion de dibujado                                                                    */
/* Parametros: Ninguno                                                                    */
/* Salida: Ninguna                                                                        */
/******************************************************************************************/
void Dibuja(void)
{
 if (!modoLinea)
 {
 int ymenor,ymayor,xmenor,xmayor;

 if (InicioX<FinX)
 {
 xmenor=InicioX;
 xmayor=FinX;
 }
 else
 {
 xmenor=FinX;
 xmayor=InicioX;
 }
 if (VentanaAlto-InicioY<VentanaAlto-FinY)
 {
 ymenor=VentanaAlto-InicioY;
 ymayor=VentanaAlto-FinY;
 }
 else
 {
 ymenor=VentanaAlto-FinY;
 ymayor=VentanaAlto-InicioY;
 }
 glBegin(GL_POLYGON);
 glVertex2i(xmenor,ymenor);
 glVertex2i(xmayor,ymenor);
 glVertex2i(xmayor, ymayor);
 glVertex2i(xmenor, ymayor);
 }
 else
 {
 /* Dibuja un segmento de linea entre el punto incial y el final */
 glBegin (GL_LINES);
 glVertex2i (InicioX, VentanaAlto-InicioY);
 glVertex2i (FinX, VentanaAlto-FinY);
 }
glEnd ();
/* Se asegura de que se ejecutan todas las ordenes */
glFlush ();
}

/******************************************************************************************/
/* Establece el area visible y el tipo de proyeccion                                      */
/* Parametros: int ancho --> Ancho del area visible                                       */
/*             int alto --> Alto del area visible                                         */
/* Salida: Ninguna                                                                        */
/******************************************************************************************/
void TamanyoVentana (int ancho, int alto)
{
 glMatrixMode(GL_PROJECTION); /* Activamos la matriz de proyeccion */
 glLoadIdentity(); /* la iniciamos con la matriz identidad */
 glOrtho(0.0, ancho, 0.0, alto, -1.0, 1.0); /* establecemos los parametros de la proyeccion */
 glViewport (0, 0, ancho, alto); /* establecemos los parametros del area de dibujo */
 VentanaAncho = ancho; /* guardamos el ancho y alto de la ventana */
 VentanaAlto = alto;
 glClear (GL_COLOR_BUFFER_BIT); /* borramos la ventana */
 InicioX = InicioY = FinX = FinY = Pulsaciones = 0; /* Inica las variables */
}

/******************************************************************************************/
/* Abre una ventana OpenGL                                                                */
/* Parametros: int numeroArgumentos --> El numero de argumentos en la llamada al programa */
/*             char ** listaArgumentos --> Vector de cadenas con cada argumento           */
/* Salida: Ninguna                                                                        */
/******************************************************************************************/
void AbreVentana (int numeroArgumentos, char ** listaArgumentos)
{
 modoLinea=TRUE;
 glutInit(&numeroArgumentos, listaArgumentos);
 glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
 glutInitWindowSize (VentanaAncho, VentanaAlto);
 glutInitWindowPosition (VentanaX, VentanaY);
 glutCreateWindow (listaArgumentos[0]);
 glutDisplayFunc (Dibuja);
 glutReshapeFunc (TamanyoVentana);
 glClearColor (0.0f, 0.0f, 0.0f, 0.0f); /* Establece el color de borrado */
 glClear (GL_COLOR_BUFFER_BIT); /* Borra el buffer de color */
 glColor3f (1.0f, 1.0f, 1.0f); /* Establece el color de dibujo */
}

void EstableceColorFondo(float r, float g, float b)
{
 cfR=r;
 cfG=g;
 cfB=b;
 glClearColor(cfR,cfG,cfB,0.0f); /* Establece el color de borrado */
 glClear (GL_COLOR_BUFFER_BIT); /* Borra el buffer de color */
 glFlush ();

}

void EstableceColorPintado(float r, float g, float b)
{
 cpR=r;
 cpG=g;
 cpB=b;
 glColor3f(cpR, cpG, cpB);
}

/******************************************************************************************/
/* Define las acciones tras una pulsacion del teclado                                     */
/* Parametros: unsigned char key --> Codigo de la tecla pulsada                           */
/*             int x --> Coordenada x del cursor en el momento de pulsar la tecla         */
/*             int y --> Coordenada y del cursor en el momento de pulsar la tecla         */
/* Salida: Ninguna                                                                        */
/******************************************************************************************/
void TecladoCallback (unsigned char tecla, int x, int y)
{
 switch (tecla)
 {
 case 27 : /* Codigo de la tecla de Escape */
 exit(0);
 break;

 case 'c':
 if (modoLinea==TRUE)
 {modoLinea=FALSE;
 printf("Cambiando a nodo cuadrados\n");
 }
 else
 {modoLinea=TRUE;
 printf("Cambiando a modo lineas\n");
 }
 case 'r' :
 EstableceColorPintado(1.0f, 0.0f, 0.0f);
 break;

 case 'g' :
 EstableceColorPintado(0.0f, 1.0f, 0.0f);
 break;

 case 'b':
 EstableceColorPintado(0, 0, 1);
 break;

 case 'y':
 EstableceColorPintado(1,1,0);
 break;

 case 'R':
 EstableceColorFondo(1.0f, 0.0f, 0.0f);
 break;

 case 'G':
 EstableceColorFondo(0.0f, 1.0f, 0.0f);
 break;

 case 'B':
 EstableceColorFondo(0.0f, 0.0f, 1.0f);
 break;

 case 'Y':
 EstableceColorFondo(1.0f, 1.0f, 0.0f);
 break;
 }
}

/******************************************************************************************/
/* Funcion de callback de las acciones del raton                                          */
/* Parametros: int boton --> Codigo del boton que se ha pulsado                           */
/*             int estado --> Estado del boton                                            */
/*             int x --> Coordenada x del cursor en el momento de pulsar la tecla         */
/*             int y --> Coordenada y del cursor en el momento de pulsar la tecla         */
/* Salida: Ninguna                                                                        */
/******************************************************************************************/
void RatonCallback (int boton, int estado, int x, int y)
{
 printf("boton %d estado %d coordenadas: %d,%d\n",boton,estado,x,y);
 if (boton == GLUT_LEFT_BUTTON)
 {

 if (estado == GLUT_DOWN) /* Si el numero de pulsaciones es par, el punto es el inicial */
 {                         /* del segmento */
 InicioX = x;
 InicioY = y;
 printf("esperando levatamiento\n");
 }
 if (estado == GLUT_UP)
 {
 printf("raton levantado\n");
 FinX = x;
 FinY = y;
 glutPostRedisplay ();
 }
 Pulsaciones ++;
 }
 if (boton == GLUT_RIGHT_BUTTON && estado== GLUT_DOWN)
 {
 glClear (GL_COLOR_BUFFER_BIT); /* Borra el buffer de color */
 glutPostRedisplay ();
 }
}

void ratonMoviendose (int x, int y)
 {
 printf("Raton Moviendose: %d,%d \n",x,y);
 //borramos linea anterior
 //guardamos colores de pintado actial
 float cplr,cplg,cplb;
 cplr=cpR;
 cplg=cpG;
 cplb=cpB;
 //cambiamos el color de pintado a negro
 EstableceColorPintado(cfR, cfG, cfB);
 //pintamos una linea del color de fondo en las coordenadas anteriores
 Dibuja();
 //volvemos al aterior color de pintado
 EstableceColorPintado(cplr, cplg, cplb);
 //cambiamos las coordenadas de fin
 FinX = x;
 FinY = y;
 //Dibujamos
 Dibuja();

 }

/******************************************************************************************/
/* Inicia las funciones de callback                                                       */
/* Parametros: Ninguno                                                                    */
/* Salida: Ninguna                                                                        */
/******************************************************************************************/
void IniciaFuncionesCallback (void)
{
 glutKeyboardFunc (TecladoCallback);
 glutMouseFunc (RatonCallback);
 glutMotionFunc(ratonMoviendose);
}

/******************************************************************************************/
/* Funcion principal                                                                      */
/* Parametros: int numeroArgumentos --> El numero de argumentos en la llamada al programa */
/*             char ** listaArgumentos --> Vector de cadenas con cada argumento           */
/* Salida: Un entero que se devuelve al sistema al acabar la ejecucion del programa       */
/******************************************************************************************/
int main(int numArgumentos, char ** listaArgumentos)
{
 /* Crea la ventana de la aplicaci¢n */
 printf("inicializando referencia\n");
 AbreVentana (numArgumentos, listaArgumentos);
 IniciaFuncionesCallback ();

 /* Establece el bucle principal de control de OpenGL */
 glutMainLoop();

 return (0);
}

Practica 1 Informática Gráfica ultimo ejercicio

El codigo aqui:

/*
* File:   main.c
* Author: luzem
*
* Created on 17 de marzo de 2010, 1:08
*/

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &quot;GL/freeglut.h&quot; //Alternativa a Glut.h
/******************************************************************/
/* Funcion de dibujado                                            */
/* Parametros: Ninguno                                            */
/* Salida: Ninguna                                                */
/******************************************************************/
void Dibuja(void) {
float altura = 1;
float base = -0.5f;
/* Establece el color de borrado */
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

/* Borra el buffer de color */
glClear(GL_COLOR_BUFFER_BIT);

/* Establece el color de dibujo */
glColor3f(1.0f, 1.0f, 1.0f);

/* Crea un poligono 2D (cuadrado) */
glBegin(GL_POLYGON);
glVertex2f(-0.5f, -0.5f);
glVertex2f(-0.5f, 0.5f);
glVertex2f(0.5f, 0.5f);
glVertex2f(0.5f, -0.5f);
glEnd();
int x;
for (x = 0; x &lt; 5; x++) {
if (x % 2 == 0) {
//cuadrado par pintamos de negro
glColor3f(0, 0, 1);
} else {
//cuadrado impar pintamos de verde
glColor3f(0, 1, 0);
}
glBegin(GL_POLYGON);
glVertex2f(base, base);
glVertex2f(base, base + altura);
glVertex2f(base + altura, base + altura);
glVertex2f(base + altura, base);
glEnd();
altura = altura - 0.2;
base = base - 0.02;
}

/* Se asegura de que se ejecutan todas las ordenes */
glFlush();
}

/******************************************************************************************/
/* Establece el area visible                                                              */
/* Parametros: int ancho --&gt; Ancho del area visible                                       */
/*             int alto --&gt; Alto del area visible                                         */
/* Salida: Ninguna                                                                        */

/******************************************************************************************/
void TamanyoVentana(int ancho, int alto) {
glViewport(0, 0, ancho, alto);
}

/******************************************************************************************/
/* Inicia las propiedades de la vista                                                     */
/* Parametros: Ninguno                                                                    */
/* Salida: Ninguna                                                                        */

/******************************************************************************************/
void IniciaVista(void) {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
}

/******************************************************************************************/
/* Abre una ventana OpenGL                                                                */
/* Parametros: int numeroArgumentos --&gt; El numero de argumentos en la llamada al programa */
/*             char ** listaArgumentos --&gt; Vector de cadenas con cada argumento           */
/* Salida: Ninguna                                                                        */

/******************************************************************************************/
void AbreVentana(int numeroArgumentos, char ** listaArgumentos) {
glutInit(&amp;numeroArgumentos, listaArgumentos);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowSize(500, 500);
glutInitWindowPosition(100, 100);
glutCreateWindow(listaArgumentos[0]);
glutDisplayFunc(Dibuja);
glutReshapeFunc(TamanyoVentana);
IniciaVista();
}

/******************************************************************************************/
/* Funcion principal                                                                      */
/* Parametros: int numeroArgumentos --&gt; El numero de argumentos en la llamada al programa */
/*             char ** listaArgumentos --&gt; Vector de cadenas con cada argumento           */
/* Salida: Un entero que se devuelve al sistema al acabar la ejecucion del programa       */

/******************************************************************************************/
int main(int numArgumentos, char ** listaArgumentos) {
/* Crea la ventana de la aplicaci¢n */
AbreVentana(numArgumentos, listaArgumentos);

/* Establece el bucle principal de control de OpenGL */
glutMainLoop();

return (0);
}

El resto de la película en libreta de notas enjoy.

Hoja 3 (9 de Marzo de 2010)

P1: Calcula, paso a paso, el resultado de la función  para el autómata de la hoja anterior y la palabra de entrada w=0100110, es decir, .

w=0100110

P2: Escribe la tabla para la función de transición  del AFND con el siguiente grafo:

Calcula, paso a paso, el resultado de la función para este autómata y la palabra de entrada w=0100110, es decir, . Averigua una palabra sobre {0,1} que el autómata no acepta.

Como el autómata acepta.

Una palabra que el autómata no aceptaria seria cualquiera que acabara con mas de dos ceros.

Gracias a melasuda por la resolución.

Practica 2 TALF

Modelar la gramática en JFlap

Enunciado Practica 2

Acción
Abreviatura
S A
PAÑAL_SUCIO B
FRÍO C
CAMBIO_POSTURA D
GASES E
HAMBRE F
DOLOR G
BIENESTAR H
CONT_PS I
PATALEO_PS J
CONT_FRÍO K
CONT_CP L
BOCA_HAMBRE M
quejido a
para_quejido b
llanto_medio c
pataleo d
estiramiento_pierna e
ceño_fruncido f
encoger_cuerpo g
llanto_alto h
llanto_bajo i
giro_lento_cabeza j
ojos_abiertos k
boca_abierta l
chupeteo_pausado m
puños_apretados n
giro_rápido_cabeza o
cuerpo_relajado p
sonrisa q

Producciones

A→B|C|D|E|F|G|H
B→abI
I→abI|cJ
J→d|e|dJ|eJ
C→abK
K→abK|cfg
D→abL
L→abL|cd
E→hgf
F→ijkM
M→lM|mM|ε
G→hdnflo
H→pqm

Comprobar si las siguientes sentencias pertenecen al lenguaje generado y en caso afirmativo hallar su árbol de derivación, y averiguar qué le está pasando al bebé.

Quejido para_quejido quejido para_quejido llanto_medio pataleo pataleo
estiramiento_pierna pataleo
Se corresponde con: ababcdded  <– Acepta
Podría significar que el bebé tiene calor.

Llanto_bajo giro_lento_cabeza ojos_abiertos boca_abierta boca_abierta chupeteo_pausado chupeteo_ pausado boca_abierta
Se  corresponde con: ijkllmml <- Acepta
Podría significar que el bebé tiene sed.

Inventar una sentencia válida y otra inválida.

VALIDA
abcde
INVALIDA
acll

Construir una posible regla de producción para comunicar MIEDO

Miedo = N
N ->llanto_alto encoger_cuerpo ceño_fruncido quejido
N ->hgfa

Ejercicio 2 Teoria TALF

p1: Dados dos lenguajes y sobre el alfabeto . Anotamos con la unión, con la intersección, el complemento y con la diferencia.

Verifica o contradice:

    • Cierto: supongamos que  es un lenguaje que genera palabras de la forma  comprenderá todas las palabras sobre el alfabeto excepto las que cumplan la forma  si a  le sacamos el sublenguaje generado por  obtendremos el mismo resultado.
    • cierto:supongamos que es un lenguaje que genera palabras de la forma  y que  genera el lenguaje  por lo tanto generaran un lenguaje de la forma   por lo tanto  serán todas las combinaciones del lenguaje  excepto las que cumplan con el patrón .
      serán todas las palabras del lenguaje  excepto las que cumplan la forma .
      serán todas las palabras del lenguaje excepto las que cumplan la forma .
      por lo tanto la intersección de ambos lenguaje serán todas las palabras de   excepto las que cumplan el patrón .
    • cierto: La intersección de  y   serán las palabras comunes entre los dos sublenguajes y su complemento sera  para la segunda parte de la ecuación
    • Falso con  generaríamos palabras de la forma  y con  generaríamos palabras de la forma  .

p2: Construye un autómata finito determinista que “acepta” el lenguaje L que contiene todas las palabras (finitas) sobre el alfabeto {0, 1} con un número par de 0s y un número impar de 1s.

El modelo del autómata en JFlap lo podéis descargar en este enlace: autómata practica 2

Edit: Corregido el autómata que tenia mal un enlace, un 0 en lugar de un 1. Muchas gracias a Ymourino

Practica 1 Talf

Búsquedas basadas en Expresiones Regulares

El objetivo de la práctica es utilizar herramientas del sistema operativo como por
ejemplo Word en Windows para hacer búsquedas en archivos .doc; y greo o egrep
para buscar en archivos de texto plano.

Buscar en el “Himno Galego” o “Conxuro da Queimada” usando Word.

  • Palabras completas. Por ejemplo “de” “dos” “terra” o cualquiera que se te ocurra.
    • <na>
      <dos>
      <terra>
  • Palabras que comiencen por una letra o conjunto de ellas. Por ejemplo palabras que comiencen por “de”.
    • <de
  • Que además de comenzar por un conjunto de letras, estén al principio de una línea. Líneas que empiecen por “de”.
    • ^v<de
  • Palabras que terminen con una letra o conjunto de letras. Por ejemplo, palabras que terminen en “os” o “en”.
    • os>^l  
      en>^l
  • Que además de terminar en un conjunto de letras estén al final o al principio de una línea.
    • >as^| (no funciona)
  • Palabras que tienen dos vocales “a”.
    • [a-z,A-Z]@a[a-z,A-Z]@a

Buscar en el archivo de personas que contienes nombres de personas e información adicional sobre ellas (correo, ciudad, teléfono). Usando grep.

  • Personas que se llaman Carlos, mostrando la línea que ocupan en la lista.
    • cat personas.txt | grep -n “Carlos”
  • Personas cuyo nombre comience por las letras comprendidas entre M y S.
    • cat personas.txt | grep -n “[MS]”
  • Líneas del archivo de personas que contengan por lo menos diez letras mayúsculas consecutivas.
    • cat personas.txt | grep “[A-Z][A-Z][A-Z][A-Z][A-Z][A-Z][A-Z][A-Z][A-Z][A-Z]”
  • Líneas que tengan su dirección de correo electrónico en el servidor uvigo.
    • cat personas.txt | grep “@uvigo”
  • Personas cuyo nombre comience por la letra “E” o “M”.
    • cat personas.txt | grep “^[EM]”
  • Personas cuyo número de teléfono termine en 21.
    • cat personas.txt | grep “21$”
  • Personas cuyo prefijo de teléfono sea el 988
    • cat personas.txt | grep ” 988″
  • Otras búsquedas que se te ocurran.

Archivos conxuro himno personas

Una visión general de RMI

“Esto es una traduccion on the fly de http://java.sun.com/docs/books/tutorial/rmi/overview.html”

RMI (Java Remote Method Invocatión) es la tecnología que nos ofrece Java para ejecutar metodos en una maquina virtual remota.

Supongamos que tenemos nuestro programa ejecutándose en nuestro PC Local (Un pentium II 350) y necesitamos realizar llamar a un método que ejecuta un abultado numero de threads para calcular algo y en la oficina es todo maquinaria antigua excepto un UltraSPARC T1  que nos reduce el tiempo de espera en unos cuantos minutos.

En un escenario como el descrito anteriormente RMI se perfila como una alternativa a tener en cuenta.

Las aplicaciones que utilicen objetos remotos necesitan realizar las siguientes acciones:

  • Localizar Objetos Remotos Las aplicaciones pueden utilizar diversos mecanismos para obtener referencias de objetos remotos. Por ejemplo: Una aplicación puede registrar sus objetos remotos con las características del registro de RMI o de forma alternativa una aplicacion puede trabajar con referencias de objetos distribuidos en sus metodos locales.
  • Comunicarse con Objetos Remotos Los detalles de la comunicación entre objetos remotos son gestionadas por RMI, para el programador la comunicación remota actuá como las llamadas clásicas a métodos.
  • Cargar las definiciones de las clases para los objetos que recibe RMI permite que los objetos sean devueltos, por eso incluye mecanismos para cargar definiciones de objetos y transmitir los datos de los mismos.

La siguiente ilustración describe una aplicación distribuida que usa el registro RMI para obtener una referencia a un objeto remoto. El servidor llama al registro para asociar un nombre con un objeto remoto. El cliente busca el objeto remoto en el registro del servidor y despues llama a un metodo del objeto. La ilustración también muestra como RMI utiliza un servidor web existente para obtener las definiciones de las clases. en la comunicación cliente-servidor y viceversa cuando es necesario

esquema RMI

Ventajas de la carga dinámica de código

Una de las características centrales y únicas de RMI es la capacidad de descargar la definición de un objeto si la clase no esta definida en la maquina virtual del receptor. Todo los tipos y comportamientos de un objeto, anteriormente disponibles en una única maquina virtual, pueden ser transmitidos a otra maquina virtual. RMI transporta objetos con sus clases, el comportamiento de sus objetos no cambia cuando es enviada a otra maquina virtual, de esta manera amplia el comportamiento de la aplicación.

Interfaces remotas, objetos y metodos

Como cualquier otra aplicacion en Java una aplicacion distribuida esta compuesta de interfaces y clases. Una interfaz declara metodos, las clases implementan los metodos declarados por la interfaz y puede contener metodos adicionales. En una aplicacion distribuida algunas implementaciones pueden residir en algunas maquinas virtuales pero no en otras. Objetos con metodos que pueden ser invocados entre maquinas virtuales se llaman objetos remotos.

Un objeto se tranforma en remoto implementando la interfaz remote, que tiene las siguientes caracteristicas:

  • Una interfaz remota extiende la interz java.rmi.Remote.
  • Cada método de la interfaz declara java.rmi.RemoteException en su clausulas throws, en adicción a cualquier excepción especifica de la aplicación.

RMI trata un objeto remoto de una forma diferente a un objeto no remoto cuando el objeto es pasado entre maquinas virtuales. mas que hacer una copia de la implementacion del objeto en la maquina virtual receptora, RMI pasa un stub remoto para un objeto remoto. El stub actua como la representacion local, o proxe, para el objeto remoto y para el cliente como una referencia remota. El cliente invoca al metodo desde el stub local el cual es el reponsable de transportar la invocarcion al metodo en el objeto remoto.

Un stub para un objeto remoto implementa el mismo conjuto de interfaces remotas que el objeto remoto. Esto permite que el stub pueda ser modelado (casting) a cualquiera de las interfaces que el objeto remoto implementas. De todas formas, solo los metods definidos en la interfaz remota estan disponibles para ser invocados el la maquina virtual receptora.

Creando Aplicaciones distrribuidas usando RMI

Utilizar RMI para desarrolar una aplicacion distribuida nos obliga a incluir estos pasos generales

  • Diseñar e implementar los componentes de tu aplicación distribuida
  • Compilar los fuentes
  • Hacer las clases accesibles desde la red
  • iniciar la aplicación

Diseñar e implementar los componentes de tu aplicacion distribuida

Primeramente determina la arquitectura de tu aplicación, incluyendo que componentes son objetos locales y que componentes son acesibles de forma remota. Este paso incluye:

  • Definir las interfaces remotas. Una interfaz remota especifica que métodos puede ser invocados por el cliente. Los programas clientes usan esas interfaces remotas y no la implementación de esas interfaces. El diseño de las interfaces incluye la definicion de los tipos de objetos que seran usados como parametros y valores de retorno para esos metodos. Si alguna de esas intarfaces o clases aun no existe tu deberas definirlas.
  • Implementar los objetos remotos. Los bjetos remotos deben implementar una o mas interfaces romatas. La clase del objeto remoto debe incluir las implementaciones de las otras interfaces y metodos que esten disponibles solo de forma local. Si alguna clase local es usada para parametros o valores de retorno estas deben ser implementadas tambien.
  • Implementar los clientes. Los clientes que usan los objetos remotos pueden ser implimentadas en cualquier momentos despues de que las interfaces remotas esten definidas, incluso despues que los objetos remotos hayan sido desarrollados.

Compilar los Fuentes

Como en cualquier programa en Java, tu debes usar el compilador javac para compilar los fuentes, el codigo fuente contiene las declaraciones de las interfaces remotas, sus implementaciones, cualquier otra clase del servidor y los clases del cliente.

nota: Las versiones ateriores a la Hava Standard Edition 5.0, como paso adicional requieren la creacion de los stub usando rmic

Haciendo las Clases accesibles por la Red

En este paso tu haras que ciertas definiciones de clases sean accesibles en la red, como la definicion de interfaces remotas y sus tipos asocidaos, y la definicion para las clases que necesitan ser descargadas para los clientes o servidores. Las difiniciones de clases generalmente se hacen accesibles a traves de un servidor web.

Construyendo un Motor de computo generico

Nos centraremos en una simple, pero poderosa, aplicación distribuida llamada motor de computo. El motor decomputo es un objeto remoto en el servidor que toma tareas de los clientes, ejecuta las tareas y retorna los resultados. Las tareas estan corriendo en la misma maquina que el servidar. Este tipo de aplicaciones distribuidas puede activar un numero de maquinas cliente para hacer uso de una maquina particularmente potente o con hardware especializado.

Los aspectos noveles del motor de computo son que las tareas que ejecuta no necesitan ser definidas cuando el motor de computo esta escrito o iniciado. Nuevos tipos de tareas pueden ser creados en cualquier momento y después enviadas al motor de computo. El único requerimiento de las tareas es que sus clases implemente una interfaz en particular. El código necesario para cumplir la tarea puede ser descargado por el sistema RMI al motor de computo. Entonces, el motor de computo ejecuta la tarea usando los recursos de la maquina donde ser esta ejecutado dicho motor.

La capacidad para realizar tareas arbitrarias esta disponible por la naturaleza dinámica de la plataforma java, que es extendida a traver de la red gracias a RMI. RMI caga de forma dinámica el código de la tarea en el motor de computo de la maquina virtual de java y ejecuta la tarea sin conocimiento previo de la implementacion de la tarea. Como las aplicaciones  con la capacidad de descargar el codigo de forma dinamica, tambien llamadas aplicaciones basadas en comportamientos, estas aplicaciones requeres una infraestructura de agentes activos. Com RMI estas aplicaciones son parte del mecanismo basica para computacion distribuida de la plataforma java.

Escribiendo un servidor RMI

El servidor del motor de computo acceta tareas de los clientes, ejecuta la tarea y devuelve los resultados, el codigo del servidor consuste en una interfaz y una clases. La interfaz define los metodos que pueden ser invocados por el clientes. Esencialmente lainterfaz define la vista de los objetos remotos para los clientes, la clases nos provee de las implementaciones.

Diseñando Una Interfaz remota

El corazon del motor de computacion es un protocolo que permite que las tareas sean enviadas al mismo, el motor de computo ejecuta esas tareas y el resultado de la ejecución es enviado al cliente. Este protocolo esta introducido en las interfaces que son soportadas por el motor de computo.

Comunicación RMI

Cada interfaz contiene un único método. La interfaz remota del motor de computo, Compute, permite a las tareas ser enviadas al motor. La interfaz cliente, Task, define como el motor de computo ejecuta una tarea enviada.

La interfaz compute.Compute define la parte accesible de forma remota, el motor de computo en si mismo, Aqui pongo el codigo de la interfaz Compute:

package compute;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Compute extends Remote {
<T> T executeTask(Task<T> t) throws RemoteException;
}

Al extender la interfaz java.rmi.Remote, la interfaz compute se identifica como una interfaz cuyos métodos pueden ser invocados desde otra maquina virtual. Cualquier objeto que implementa esta interfaz puede ser un objeto remoto.

Como miembro de una interfaz remota, el método executeTask es un método remoto. De todas formas este método debe ser definido para ser capar de ignorar una java.rmi.RemoteException. Esta excepción es ignorada por el sistema RMI en la invocación de métodos remotos para indicar fallos de comunicación o errores del protocola. Una RemoteExcepcion es una excepción comprobada, pero cualquier código que invoque a un método remoto necesita manejar esta excepción con un catch o con una cláusula throws.

La segunda interfaz necesaria para el motor de computo es la interfaz Task, que es el tipo de parametro del metodo executeTask en la interfaz compute. Aqui teneis es codigo de la interfaz Task

package compute;

public interface Task<T> {
T execute();
}

La interfaz task define un único método, execute, que no tiene parámetros ni ignora ninguna excepción. Porque la interfaz no extiende a Remote y el método de esta interfaz no necesita ignoros la excepción java.rmi.RemoteException en su cláusula throws.

La interfaz Task tiene un tipo de parámetro T, que representa el tipo de resultado de las tareas a computar. Esta interfaz ejecuta métodos que devuelven el resultado del computo.

RMI usa los mecanismos de serializacion de objetos para transportar los objetos por valor entre maquinas virtuales, Para que un objeto pueda ser serializables, su definición debe implementar la interfaz java.io.Serializable. Por lo tanto las clases que implementen la inteaz Task tambien deben implementar la interfaz Serializable.

Los diferentes tipos de tareas que pueden ser ejecutadas por el Objeto Compute son implemntaciones del tipo Task. Las clases que son implementads por esta interfaz pueden contener cualquier informacion necesitada para la ejecucion de la tarea y cualquier otro metod necesario para la ejecución.

Esta es la forma por la cual RMI hace que este simple motor de computo posible. RMI asume que los objetos Task estan escritos en Java, las implementaciones de los objetos Task que eran desconocidos para el motor de computo son obtenidas por RMI baja demanda, esta capacidad permite a los clientes del motor de computo definir nuevos tipor de tareas para ejecutar en el servidor sin la necesidad de especificar nada en el codigo de la parte del servidor.

El motor de computo implementado por la clase ComputeEngine iplementa la interfaz Compute, permitiendo que distintas tareas sean enviadas a el cn llamadas al metodo executeTask. estas tareas se ejecutan usado de implementacion de Task del metodo execute y los resultados son enviados al cliente remoto.

Implementando una interfaz Remota

Una clase que implementa una intergaz emoto necesida hacer lo siguiente:

  • Declarar las interfaces remotas que implementara
  • Definir los constructores para cada objeto remoto
  • Implementar cada metodo remoto de las interfaces remotas

Un servidor RMI necesita crear los objetos remotos iniciales y exportarlos al entrono de ejecución RMI, lo que les permite al servidor recibir invocaciones remotas. Este paradigma de funcionamiento puede ser encapsulado en un metodo de la implementación del objeto remoto o ser incluida en otra clase. El paradigma de funcionamiento debe realizar la siguientes acciones:

  • Crear e instalar un gestor de seguridad
  • Crear y exportar uno o mas objetos remotos
  • Registrar al menos un objeto remoto con el registro RMI (o con otro servicio de nombe, como un sercio accesible a traves la interfaz Java Namin and Directory) como metodo de arranque.

A continuacion ponemos la implementacion del motor de computo. La clase engine.ComputeEngine implementa la interfaz remota Computo y tambien incluye el metodo main para iniciar el motor de computo. Aqui teneis el codigo fuentepara la clase ComputeEngine:

package engine;

import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import compute.Compute;
import compute.Task;

public class ComputeEngine implements Compute {

    public ComputeEngine() {
        super();
    }

    public <T> T executeTask(Task<T> t) {
        return t.execute();
    }

    public static void main(String[] args) {
        if (System.getSecurityManager() == null) {
            System.setSecurityManager(new SecurityManager());
        }
        try {
            String name = "Compute";
            Compute engine = new ComputeEngine();
            Compute stub = (Compute) UnicastRemoteObject.exportObject(engine, 0);
            Registry registry = LocateRegistry.getRegistry();
            registry.rebind(name, stub);
            System.out.println("ComputeEngine bound");
        } catch (Exception e) {
            System.err.println("ComputeEngine exception:");
            e.printStackTrace();
        }
    }
}

A continuación explicaremos cada elemento de la implementacion del motor de computo.

Declarando las interfaces remotas que serán implementadas.

La implementacion del motor de computo es declarada:

public class ComputeEngine implements Compute

Esta declaracion indica que implementamos la interfaz remota Computo y que pude ser usada par los objetos remotos.

La clase ComputeEngine define una implementacion de la clase del objeto remoto que implementa una unica interfaz remota. La clase ComputeEngine tambien contiene dos elementos executables que solo pueden ser invocados de forma local. El primero de esos elementos es el contructor para las instancias de ComputeEngine. El segundo es el metodo main que se usa para clerar la instacion de ComputeEngine que  hace que este disponible para los clientes.

Definiendo el constructor para un objeto remoto.

La clase ComputeEngine tiene un unico contructor que no toma ningun argumento. El codigo del constructor esta a continuación:

public ComputeEngine() {
    super();
}

Este constructor solo invoca al constructor de la superclase, el cual es un constructor sin argumentos de la clase Object. De todas formas el constructor de la superclase es invocado si fuera omitido por el constructor ComputeEngine, esto esta incluido para facilitar la comprension del codigo.

Creando las implementaciones para cada método remoto.

La clase para un objeto remoto implementa cada metodo remoto de la interfaz remota. La interfaz Compute contiene un unico metodo, executeTask, implementacion de la cual se muestra  a continuación

public <T> T executeTask(Task<T> t) {
    return t.execute();
}

Este metodo implementa el protocolo entre el objeto remoto ComputeEngine y sus clientes, ComputeEngine recibe de cada cliente un objeto Task con su implementacion para la interfaz Task y su metodo de ejecución. El ComputeEngine ejecuta cada tarea del cliento y devielve lo resultados de la ejecucion al cliente.

Pasando Objetos en RMI

Los argumentos o valores de retorno de los métodos remotos pueden ser de cualquier tipo, incluyendo objetos locales, objetos remotos y tipos de datos primitivos. De forma mas preciso cualquier entidad de cualquier tipo puede ser pasada o recibida desde un objeto remoto, un objero remoto, o un objeto serializable (lo que significa que implementa la interfaz java.io.Serializable).

Algunos tipos de objeto no cumple alguno de estos criterios y por lo tanto no pueden ser pasados o recibidos por un método remoto, Muchos de esos objetos, como threads o descriptos de archivos, encapsulan información que solo tiene sentido en un único espacio de memorio, Muchas de las clases del núcleo de java incluyendo las clases en los paquetes java.lang y java.util implementan la interface Serializable.

Las reglas que rigen las condiciones para que los argumentos o valores de retorno sean validos son las siguientes:
*Los objetos remotos se pasa esencialmente por referencia. Una referencia a un objeto remote es un stub, que es un proxy del lado del cliente que implementa el conjunto completo de interfaces que implementa un objeto remoto.
*Los objetos locales son pasados como una copia, usando serializacion de objetos. Por defecto , todos los campos son copiados excepto los que están marcados como static o transient, El comportamiento predeterminado de la serializacion pude ser sobrescrito.

Pasar un objeto remoto por referencia significa que cualquier cambio realizado en el estado del objeto por la invocación remota queda reflejado en lo objeto remoto original. Cuando un objeto remoto es pasado, solo las interfaces que son interfaces remotas están disponibles para el receptor. Cualquier motoso definido en la implementación de la clase o definido en las interfaces no remotas implementadas por la clase no estan disponibles para el receptor.

Por ejemplo, if estuvieramos pasando una referencia a una instancia de la clase ComputeEngine, el receptor solo tendria acceso al metodo executeTask. El receptor no veria el constructor de ComputeEngine, su metodo main o sun implementaciones de cualquier metodo de java.lang.Object.

En los parámetros y valores de retorno de las invocaciones remotas a metodos, los objetos que no son objetos remotos son pasados por valor, osea , una copia del objeto es creada en la maquina virtual receptora. Cualquier cambio en el estado del objeto por el receptor se ve reflejado unacamente en la copia del receptor, no en la instancia original enviada. Cualquier cambio en el estado del objeto realizado por el enviador se ve reflejado solo en la instancia original del envio, no en la copia del receptor.

Implementando el metodo main del Servidor

El metodo mas complejo de la implementacion de ComputeEngine es el metodo main. El metodo main es usado para iniciar el motor de computo y necesita realizar la inicializacion para preparar el servidor para aceptar llamadas de los clientes. Este metodo no es un metodo remoto, lo que significa que no puede ser invocado por una maquina virtual diferente. A causa que que el metodo main esta declarado como static, el metodo no esta asociado con el objeto o mejor dicho con la clase ComputeEngine.

Creando e instalando un gestor de seguridad

La primera tarea del metodo main es crear e instalar un gestor de seguridad (Security Manager), ue protege el aceso a los recursos del sistema de codigo no verificado que transcurre entre maquinas virtuales. Un gestor de seguridad determina que codigo descargado tiene acceso al sistema de archivos locales o puede realizar cualquier otra operación privilegiada.

Si un programa RMI no instala un gestor de seguridad, RMI no descargara clases (que no esten en su class path) para los objetos recibidos como argumentos o valores de retorno de los metodos remotos. Esta restricción nos asegura que las operaciones realizadas con condigo descargado estan sujetas a una politica de seguridad.

Aqui esta el codigo que crea e instala un gestor de seguridad:

if (System.getSecurityManager() == null) {
 System.setSecurityManager(new SecurityManager());
}

Haciendo los objetos remotos disponibles para los clientes

El metodo main clea una instancia de ComputeEngine y lo exporta para el entono de ejecucion de RMI con las sigioentes lineas


Compute engine = new ComputeEngine();
Compute stub =

(Compute) UnicastRemoteObject.exportObject(engine, 0);

El metodo estatico UnicastRemoteObject.exportObject exporta el objeto remodo pasado como parametro para que pueda recibir invocaciones de sus metodos remotos a traves de clientes remotos. El segundo argumento, un int, especifica que puerto TCP sera usado para escuchar solicitudes para las invocaciones remotas. Generalmente el valor es cero, que especifica el uso de un puerto anónimo. El puerto actual sera seleccionado por RMI en tiempo de ejecución o por el sistema operativo. De todas formas un valor distinto a  cero puede ser usado para especificar el puerto de escucha. Una vez que la invocación  a exportObject se ha realizado de forma satisfactoria, el objeto remoto ComputeEngine esta listo para recibir invocaciones remotas.

El metodo exportObject devuelve un stub del objeto remoto esportado. Fijate que el tipo de variable stub debe ser Compute, no ComputeEngine, porque el stub de un objeto remoto solo implementa las interfaces remotasde los objetos remotos exportados implementados.

El exportObject motodo declara que puede ignorar una RemoteException, que es un tipo de excepcion comprobada. El metodo main maneja esta exception con sus bloques try/catch. Si la excepcion no esta manejada de esta manera el metodo main debera ignorar (throws) la RemoteException si lo recursos necesarios no estan disponibles, como que el puerto este ocupado para otros objetivos.

Antes de que el cliente pueda invocar un metodo de un objeto remoto, primero se debe obtener una referencia al objeto remoto.  Para obtener esta referencian se puede hacer de la misma forma por la que cualquier referenca a un objeto es obtenida por el programa, como obtener l referencia como parte de un valor de retorno o como parte de una estructura de datos que contiene dicha referencia.

El sistema nos provee de un tipo de objeto remoto particular, el registro RMI, para encontrar referencias a otros objetos remotos. El registro RMI es un servicio de nombres de objeto que permite a los clientes obtener una referencia a un objeto remoto por su nombre. El registro es usado generalmente para encontrar el primer objeto remoto que el cliente RMI necesita usar. Ese primer objeto puede ser usado para encontrar otros objetos.

La interfaz java.rmi.registry.Registry es el API para registrar y buscar objetos remotos en el registro. La clase java.rmi.registry.LocateRegistry contiene metodos estaticos para sintetizar una referencia remota a un registro en una direccion de red particular (host y puerto). Estos metodos crean la referencia remota a un objeto que contiene la direccion de red pero sin realizar ninguna comunicacion remota. LacaleRegistry tambien contiene metodos estaticos para crear un nuevo registro en la maquina virtual actual, de todas formas este ejemplo no usa esos metodos. Una vez que el objeto remoto esta registrado en el registro RMI del host local, los clientes de cualquier host puede bscar el objeto remoto por su nombre, obteniendo ss referencias y luego invocar los metodos remotos del objeto. El registro puede ser compartido por todos los servidores de un host, o un unico proceso servidor puede crear su propio registro.

La clase ComputeEngine crea un nombre para el objeto con la siguiente linea:

String name = "Compute";

Este codigo añade el nombre al registro RMI del servidor. Este para se reliza antes de los siguientes comando

Registry registry = LocateRegistry.getRegistry();
registry.rebind(name, stub);

La llamada a rebind realiza una llamada al registro RMI del host local. Como cualquier llamada remota, esta puede generar una RemoteExceptionque sera manejada en bloque catch del metodo main.

Ten en cuenta las siguientes condiciones el las llamadas a Registry.rebind:

  • La sobrecarga sin argumentos a LocateRegistry.getRegistry sintetiza una referencia a iun registroen el host local y en su puerto predeterminado,1099. Tu debes usar una sobrecarga que tiene un parametro int si el registro esta creado en un puerdo distinto al 1099.
  • Cuando se realia una invocacion al registro, se pasa un stub del objeto remoto en lugar de una copia del objeto remoto. Las implementaciones de objetos remotos, como las instancias de ComputeEngine, nunca abandonaran la maquina virtual donde fueron creadas. Asi que, cuando un cliente realiza una busqueda en un servidor de registro de objetos remotos, una copia del stub (boceto) es devuelta. Los objetos remotos generalente son mas efecientes pasados como una referencia remota en lugar que por valor
  • Por razones de seguridad, una aplicacion solo puede realizar operaciones bind, unbind o rebind con referencias a objetos con un registro ejecutandosse en el mismo host. Esta restriccion nos ayuda a evitar que un cliente remoto borre o sobreescriba alguna estrada del servidor de registro. De todas formas una lookup (busqueda), puede ser solicitada por cualquier host, local o remoto.

Una vez que el servidor ha sido registrado con el registro local RMI, muestra un mensaje indicando que esta listo para empezar a manejar llamadas. Entonces, el metodo main se completa. No es necesario usar threads para mantener el servidor manejando escuchas.Mientras exista referencia al objeto ComputeEngine en otra maquina virtual, local o remota, el objeto ComputeEngine no se destruirá ni le afectara el recolector de basura, Porque el programa maneja una referencia a ComputeEngine que esta en el registro, que es accesible por el cliente remoto. El sistema RMI mantiene mantiene los procesos ComputeEngine ejecutandose. El ComputeEngine esta disponible para aceptar llamadas y no será utilizado hasque que su enlace sea eliminado por el registro y los clientes no remotos rompan la referencia remota al objeto CompteEngine.

La ultima porción de código en el método  ComputeEngine.main manejo cualquier posible excepción. El único tipo de excepción comprobada que puede ser ignorada en el código es la RemoteException, a través la llamada a UnicastRemoteObject.exportObject o por la llamada al método rebind. De cualquier forma, el programa no puede hacer mucho mas que salir después de imprimir un mensaje de error. En algunas aplicaciones distribuidas es posible recuperarse del fallo para realizar llamadas remotas. Por ejemplo, la aplicación podría hacer un reintento o seleccionar otro servidor para continuar funcionando.

Creando un programa Cliente

El motor de computo (compute engine) es un programa relativamente simple. Ejecuta tareas que son manejadas para eso. Los clientes de motor de computo son mas complejos. Un cliente necesita llamar al motor de computo, pero también tiene que definir las tareas que se ejecutarán en el motor de computo.
Dos clases separadas forman el cliente de nuestro ejemplo. La primera clase, ComputePi, busca e invoca un objeto Compute. La segunda clase, Pi, implementa la interfaz Task y define el trabajo para ser realizado por el motor de computo. El trabajo de la clase Pi es computar el valor de algunos decimales de Pi .

La interfaz no remota Task esta definida a continuación:

package compute;

public interface Task<T> {
 T execute();
}

Aquí estas el código fuente para cliente-ComputePi (link) :

<pre>package client;

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.math.BigDecimal;
import compute.Compute;

public class ComputePi {

 public static void main(String args[]) {
 if (System.getSecurityManager() == null) {
 System.setSecurityManager(new SecurityManager());
 }
 try {
 String name = "Compute";
 Registry registry = LocateRegistry.getRegistry(args[0]);
 Compute comp = (Compute) registry.lookup(name);
 Pi task = new Pi(Integer.parseInt(args[1]));
 BigDecimal pi = comp.executeTask(task);
 System.out.println(pi);
 } catch (Exception e) {
 System.err.println("ComputePi exception:");
 e.printStackTrace();
 }
 }
}

Igual que el servidor ComputeEngine, el cliente comienza instalando un gesto de seguridad. Este paso es necesario porque durante el proceso de recibir los borradores (stub) de los objetos remotos del servidor puede ser necesario descargar las definiciones de clases del servidor. Para que RMI descargue clases el gesto de seguridad debe ser forzado.
Después de instalar el gestor de seguridad, el cliente construye un nombre para realizar las búsquedas de los objetos remotos Compute, usando el mismo nombre que usa ComputeEngine para unirse con el cliente. También el cliente usa la API LocaleRegistry.getRegistry para sintetizar una referencia remota para registras el host remoto. El valor de primer argumento de los argumentos pasados por consola, args[0], es el nombre del host remoto en el que se ejecuta el objeto Compute. Entonces el cliente invoca al método lookup en el registro para buscar el objeto remoto por nombre en el registro del host servidor. La sobrecarga particular de LocateRegistry.getRegistry  que solo tiene un parámetro una String, devuelve una referencia al registro del host remoto en el puerto 1099. Si el puerto del servidor es distinto al 1099 debes usar la sobrecarga que tiene un parámetro int.

A Continuación el cliente crea un nuevo objeto Pi, pasando al constructor de pi el valor del segundo argumento de los argumentos de la linea de comandos, args[1], pasada como un entero. Este argumento indica el numero de valores decimales para ser calculados. Finalmente el cliente invoca al método executeTask recibiendo un objeto del tipo BigDecimal, que el programa almacena en la variable result. El siguiente gráfico describe el flujo entre el cliente ComputePi, el rmiregistry (registro mi) y el ComputeEngine (motor de computo).

Flujo elementos RMI

La clase Pi implementa la interfaz Task y calcula el valor de un numero de decimales de Pi. Para este ejemplo, el algoritmo no es importante. Lo que si es importante del algoritmo es su coste computacional lo que significa que se deberá ejecutar en un servidor capacitado.
Aquí esta el código fuente para el cliente.Pi la clase que implementa la interfaz Task:

</pre>
package client;
import compute.Task;

import java.io.Serializable;

import java.math.BigDecimal;
public class Pi implements Task<BigDecimal>, Serializable {
 private static final long serialVersionUID = 227L;
 /** constants used in pi computation */

private static final BigDecimal FOUR =

BigDecimal.valueOf(4);
 /** rounding mode to use during pi computation */

private static final int roundingMode =

BigDecimal.ROUND_HALF_EVEN;
 /** digits of precision after the decimal point */

private final int digits;
 /**

* Construct a task to calculate pi to the specified

* precision.

*/

public Pi(int digits) {

this.digits = digits;

}
 /**

* Calculate pi.

*/

public BigDecimal execute() {

return computePi(digits);

}
 /**

* Compute the value of pi to the specified number of

* digits after the decimal point.  The value is

* computed using Machin's formula:

*

*          pi/4 = 4*arctan(1/5) - arctan(1/239)

*

* and a power series expansion of arctan(x) to

* sufficient precision.

*/

public static BigDecimal computePi(int digits) {

int scale = digits + 5;

BigDecimal arctan1_5 = arctan(5, scale);

BigDecimal arctan1_239 = arctan(239, scale);

BigDecimal pi = arctan1_5.multiply(FOUR).subtract(

arctan1_239).multiply(FOUR);

return pi.setScale(digits,

BigDecimal.ROUND_HALF_UP);

}

/**

* Compute the value, in radians, of the arctangent of

* the inverse of the supplied integer to the specified

* number of digits after the decimal point.  The value

* is computed using the power series expansion for the

* arc tangent:

*

* arctan(x) = x - (x^3)/3 + (x^5)/5 - (x^7)/7 +

*     (x^9)/9 ...

*/

public static BigDecimal arctan(int inverseX,

int scale)

{

BigDecimal result, numer, term;

BigDecimal invX = BigDecimal.valueOf(inverseX);

BigDecimal invX2 =

BigDecimal.valueOf(inverseX * inverseX);
 numer = BigDecimal.ONE.divide(invX,

scale, roundingMode);
 result = numer;

int i = 1;

do {

numer =

numer.divide(invX2, scale, roundingMode);

int denom = 2 * i + 1;

term =

numer.divide(BigDecimal.valueOf(denom),

scale, roundingMode);

if ((i % 2) != 0) {

result = result.subtract(term);

} else {

result = result.add(term);

}

i++;

} while (term.compareTo(BigDecimal.ZERO) != 0);

return result;

}

}
<pre>

Notese que todas las clases serializables, aquellas que implementan las interfaces serializable directa o indirectamenta, deben declarar un campo private static final llamado seriaVersionUID para garantizar la compativilidad entre versiones. Si no en existe una versión previa de la clase, entonces el valor de este campo puede ser cualquier valor long, similar al de 227L usado por pi, segun la longitud del valor sera usado en versiones futuras. Si existiese una versión previa de la clase si una declaracion explicita de serialVersionUID , pero la compatibilidad con la versión anterior es importante, entonce el valor implícito predeterminado para computar sera el que debera ser usado para el valor de la nueva versión. La herramienta serialver puede determinar el valor predeterminado a usar.

La caracteristica mas importante de este ejemplo es que la implementación del objeto value nunca necesita la definición de la clase Pi hasta que el objeto es pasado como argumento al metodo executeTask. En ese punto, el codigo de la clase es cargado por RMI en la maquina virtual del Objeto Compute, el metodo execute es invocado, y el codigo de la tarea es ejecutado. El resultado, que en el caso de la tarea Pi es un objeto BigDecimal, es retornado al cliente que realizao la llamado, donde es usado para imprimir el resultado del computo.

El hecho de que el objeto Task  compute el valor de Pi es irrelevante para el objeto ComputeEngine. Tu tambien podrias implementar una task que, por ejemplo, generase un numero primo aleatoria. Cualquier tarea podria se intensiva desde el punto de vista computacional  seria una buena candidata para el ComputeEngine, pero requeriria un codigo muy diferente. Este codigo tambien podria ser descargado cuando un objeto Task es pasado a otro Compute. a causa de que el objeto Compute no necesita saber lo que la implementación de Task hace.

proximo post (como poner todo esto en marcha)