Es mostren els missatges amb l'etiqueta de comentaris eines. Mostrar tots els missatges
Es mostren els missatges amb l'etiqueta de comentaris eines. Mostrar tots els missatges

divendres, 28 d’octubre del 2011

Com instal·lar XAMPP a Ubuntu.

A l'hora de fer presentacions i demos d'aplicacions web, pot ser molt útil disposar d'una versió de la demo en un servidor web en localhost, potser en una clau usb.

També pot ser molt útil un servidor web d'aquest tipus a l'hora de preparar sessions de formació.

És possible trobar per Internet diverses distribucions que d'aquest tipus que combinen Apache amb  MySQL i PHP, tant sobre Linux (LAMP), com sobre Windows (WAMP), per exemple XAMPP o MoWeS Portable.

MoWeS Portable empaqueta, a més de la plataforma AMP, altres aplicacions que utilitzen aquesta infraestructura, com Joomla! o Drupal. Ara bé, MoWeS Portable només està disponible per a Windows.

XAMPP, en canvi, s'ofereix per a Linux i Windows.

En el post d'avuí el que faig és instal·lar el XAMPP a un netbook Linux.

No cal dir que la plataforma AMP es pot instal·lar des del Centre de Programari de l'Ubuntu. Però la instal·lació de XAMPP és encara més senzilla.

Dit i fet. El primer pas és descarregar-se el paquet d'instal·lació. El  puc trobar en aquesta adreça:  http://sourceforge.net/projects/xampp/files/BETAS/xampp-linux-1.7.7.tar.gz/download. Són uns 77MB.

Aquesta versió incorpora:   Apache, MySQL, PHP amb PEAR, Perl, ProFTPD, phpMyAdmin, OpenSSL, GD, Freetype2, libjpeg, libpng, gdbm, zlib, expat, Sablotron, libxml, Ming, Webalizer, pdf class, ncurses, mod_perl, FreeTDS, gettext, mcrypt, mhash, eAccelerator, SQLite i IMAP C-Client.

En particular Apache 2.2.21, MySQL 5.5.16, PHP 5.3.8, i phpMyAdmin 3.4.5.

Per instal·lar  n'hi ha prou amb desempaquetar el tar.gz. Cal tenir en compte que als fitxers d'instal·lació s'assumeix que la home del XAMPP serà /opt/lampp. Això NO vol dir que només es pugui instal·lar en aquesta carpeta. Això l'únic que vol dir és que caldrà que /opt/lampp sigui la "home" del XAMPP i això es podrà aconseguir amb un enllaç simbòlic des de /opt/lampp al directori d'instal·lació que triï.

Si la carpeta de destinació del XAMPP es troba en una clau usb caldrà que aquesta clau estigui formatada amb un sistema de fitxers que permeti els enllaços simbòlics, per exemple  ext2,ext3 o ext4.

A l'hora de fer l'enllaç simbòlic caldrà tenir en compte el punt de muntatge del dispositiu. Per exemple, al meu netbook amb Ubuntu 11.04, el punt de muntatge és /media.

Per a poder instal·lar correctament el XAMPP caldran, a més, permisos de root. I també per a posar en marxa i aturar els diversos components de la distribució.

Tenint en compte tot l'anterior, instal·lo el XAMPP a la meva carpeta home, on he deixat el tar.gz:

sudo tar xvfz xampp-linux-1.7.7.tar.gz -C .

Amb la ordre anterior es crea una carpeta lampp amb els dversos components del XAMPP.

Ara creo l'enllaç simbòlic:

sudo ln -s /home/albert/lampp /opt/lampp

I engego per primer cop el servidor. Aquesta primera arrencada acaba d'instal·lar i configurar els components.

albert@athena:~$ sudo /opt/lampp/lampp start
Starting XAMPP for Linux 1.7.7...
XAMPP: Starting Apache with SSL (and PHP5)...
XAMPP: Starting MySQL...
XAMPP: Starting ProFTPD...
XAMPP for Linux started.
albert@athena:~$ 

I per revisar la nova instal·lació en tinc prou amb obrir un navegador i apuntar-lo al localhost:




El directori arrel del servidor web es troba a /opt/lampp/htdocs (en el meu cas, mercès a l'enllaç simbòlic, és /home/albert/lampp/htdocs).

Per aturar el XAMPP puc fer:


albert@athena:~$ sudo /opt/lampp/lampp stop
Stopping XAMPP for Linux 1.7.7...
XAMPP: Stopping Apache with SSL...
XAMPP: Stopping MySQL...
XAMPP: Stopping ProFTPD...
XAMPP stopped.
albert@athena:~$ 

I per a eliminar la instal·lació sense deixar rastres, amb el XAMPP aturat, n'hi ha prou amb fer:

sudo rm -r lampp

És possible engegar i aturar individualment els diversos components del XAMPP. Examinant el fitxer $HOME/lampp/lampp es poden  trobar les diverses opcions disponibles. La forma d'invocar-les, en general, és: 

sudo /opt/lampp/lampp opció

diumenge, 18 de setembre del 2011

Taules hash amb C a Linux

Les taules hash , hash maps són una estructura de dades que, mitjançant una funció hash (o funció resum) identifica uns valors, anomenats claus, amb uns valors associats.

L'avantatge de les taules hash sobre altres estructures de dades és la gran rapidesa d'accés a la informació i el cost constant d'aquest accés. Per això,  les taules hash tenen un munt d'aplicacions.

En tot cas, les taules hash són una solució molt senzilla i eficient per a l'emmagatzematge i l'accés a la informació que es pugui modelar directament amb taules de  parelles clau valor.

Amb C sobre Linux (en el meu cas Ubuntu 11.04) apareixen almenys tres opcions ràpides per a implementar taules hash:
Amb funcions de la llibreria estàndar GlibC
Amb funcions de la llibreria GTK, Glib

Amb "bases de dades" del tipus DBM, a Linux Gdbm; o amb l'evolució amb capacitat concurrent de Gdbm desenvolupada per a SAMBA: TDB

Anem a repassar les opcions:


Amb Glibc (La llibreria estàndard de C)

Un exemple de taules hash amb les funcions de la llibreria estàndard Glibc (hcreate, hsearch i hdestroy)

/* aquest exemple està adaptat de la documentació del man */
#include <stdio.h>
#include <search.h>
#include <string.h>

struct info {        /* aquesta estructura és la dels valors emmagatzemats a la taula */
    int age, room;  
};

#define NUM_EMPL    5000    /* # número d'elements. */

int main(void)
{
    char string_space[NUM_EMPL*20];       /* espai d'emmagatzematge de la taula de valors de les claus. */
    struct info info_space[NUM_EMPL];     /* espai per emmagatzemar la informació de claus. */
    char *str_ptr = string_space;         /* següent posició a l'espai de valors. */
    struct info *info_ptr = info_space;   /* següent posició a l'espai de claus. */
    ENTRY item;
    ENTRY *found_item;                    /* nom a buscar. */
    char name_to_find[30];
    int i = 0;

    /* Create table; no error checking is performed. */
    printf("Crea la taula\n");
    (void) hcreate(NUM_EMPL);

    printf("Introdueix clau age room a la taula. Per acabar d'entrar valors prem CTRL-D\n");
    while (scanf("%s %d %d", str_ptr, &info_ptr->age,
           &info_ptr->room) != EOF && i++ < NUM_EMPL) {


        /* Put information in structure, and structure in item. */
        printf("Afegeix %s %d %d a la taula\n", str_ptr, info_ptr->age, info_ptr->room);
        item.key = str_ptr;
        item.data = info_ptr;
        str_ptr += strlen(str_ptr) + 1;
        info_ptr++;


        /* Put item into table. */
        printf("Afegeix - hsearch(item, ENTER)\n");
        (void) hsearch(item, ENTER);
    }



    /* Access table. */
    printf("Busca valors a la taula. Per acabar CTRL-C\n");
    item.key = name_to_find;
    while (scanf("%s", item.key) != EOF) {
        printf("Buscant %s amb hsearch(item, FIND)\n", item.key);
        if ((found_item = hsearch(item, FIND)) != NULL) {
           
            /* If item is in the table. */
            (void)printf("trobat %s, age = %d, room = %d\n",
                found_item->key,
                ((struct info *)found_item->data)->age,
                ((struct info *)found_item->data)->room);
        } else
            (void)printf("no trobat %s\n", name_to_find);
    }
    return 0;
}


Una excució podria ser, per exemple:


albert@atenea:~/wk-c/prova-hash-glibc$ ./prova_hash_glibc
Crea la taula
Introdueix clau age room a la taula. Per acabar d'entrar valors prem CTRL-D
clau1 1 2
Afegeix clau1 1 2 a la taula
Afegeix - hsearch(item, ENTER)
clau2 3 4
Afegeix clau2 3 4 a la taula
Afegeix - hsearch(item, ENTER)
clau3 5 6
Afegeix clau3 5 6 a la taula
Afegeix - hsearch(item, ENTER)
Busca valors a la taula. Per acabar CTRL-C
prova
Buscant prova amb hsearch(item, FIND)
no trobat prova
clau1
Buscant clau1 amb hsearch(item, FIND)
trobat clau1, age = 1, room = 2
clau3
Buscant clau3 amb hsearch(item, FIND)
trobat clau3, age = 5, room = 6
^C
albert@atenea:~/wk-c/prova-hash-glibc$



Amb GLib (la llibreria de GTK)

La referència per a taules hash amb Glib es troba a http://developer.gnome.org/glib/2.28/glib-Hash-Tables.html

En resum:

Per a crear una taula hash de Glib (una GHashTable), fem servir: g_hash_table_new().

Per afegir una clau i el seu valor associat a la GHashTable, fem servir: g_hash_table_insert().

Per a cercar el valor associat a una clar fem servir  g_hash_table_lookup()  i g_hash_table_lookup_extended().

Per a eliminar una aprella clau-valor, g_hash_table_remove().

Per a  invocar una funció per a cada parella clau-valor,  g_hash_table_foreach().

Per a esborrar del tot una GHashTable,  g_hash_table_destroy().


Vet aquí l'exemple amb GLibC implementat amb les funcions de GLib. L'exemple ha estat desenvolupat fent servir Anjuta. He partit d'un projecte genèric mínim amb C. M'ha calgut afegir la llibreria glib2.0-0 i l'include a glib.h al fitxer Makefile.am (automake)

## Process this file with automake to produce Makefile.in

## Created by Anjuta

AM_CPPFLAGS = \
    -DPACKAGE_DATA_DIR=\""$(datadir)"\"

AM_CFLAGS =\
     -Wall\
     -g\
     -I/usr/include/glib-2.0\
     -I/usr/lib/glib-2.0/include
     
bin_PROGRAMS = hashtables

hashtables_SOURCES = \
    main.c

hashtables_LDFLAGS = -lglib-2.0

hashtables_LDADD =



El codi és el següent. Pràcticament només he tingut que canviar les funcions:


#include <stdio.h>
#include <string.h>
#include <glib.h>


struct value {        /* aquesta estructura és la dels valors emmagatzemats a la taula */
    gint age, room;  /* gint és el tipus glib que encapsula a int*/ 
};

#define NUM_KEYS    5000    /* número d'elements. */
#define KEY_SIZE    20      /* tamanys de la clau*/

int main(void)
{
    GHashTable *hashTable;

    gchar keys[NUM_KEYS * KEY_SIZE];    /* espai d'emmagatzematge de la taula de valors de les claus. */
    struct value values[NUM_KEYS];      /* espai per emmagatzemar la informació de claus. */
    gchar *keyPtr = keys;               /* següent posició a l'espai de valors. */
    struct value *valuePtr = values;    /* següent posició a l'espai de claus. */
    gchar keyToFind[KEY_SIZE];          /* clau a buscar, màxim de KEY_SIZE caràcters */
    int i=0;                   

    /* Crea taula hash.
      g_str_hash és una funció hash que es proporciona amb Glib
      per a claus del tipus (gchar *)
      g_str_equal és una funció que es proporciona amb Glib per verificar
      l'igualtat de dues claus del tipus (gchar *)
    */
    printf("Crea la taula\n");
    hashTable = g_hash_table_new(g_str_hash, g_str_equal);

    printf("Introdueix clau age room a la taula. Per acabar d'entrar valors prem CTRL-D\n");
    while (scanf("%s %d %d",
                 keyPtr,
                 &valuePtr->age,
                 &valuePtr->room) != EOF && i++ < NUM_KEYS) {
                 
        /* traça */
        printf("Afegeix %s %d %d a la taula\n", keyPtr, valuePtr->age, valuePtr->room);

        /* els posa a la taula */
        printf("Afegeix - g_hash_table_insert\n");
        /* afegeix clau-valor a taula hash.
          g_hash_table_insert rep tres paràmetres
          punter a la taula hash
          punter a la clau
          punter al valor
        */        
        g_hash_table_insert(hashTable, keyPtr, valuePtr);

        /* prepara els punters per a nova clau valor*/
        keyPtr += strlen(keyPtr) + 1;
        valuePtr++;
    }


    /* Ara busca per la taula. */
    printf("Busca valors a la taula. Per acabar CTRL-C\n");
    keyPtr = keyToFind;
    while (scanf("%s", keyPtr) != EOF) {
        printf("Buscant %s amb g_hash_table_lookup\n", keyPtr);
        if ((valuePtr = g_hash_table_lookup(hashTable, keyPtr)) != NULL) {
           
            /* si ha trobat diferent de nul, el mostra. */
            /* nota: això pot ser un problema, perquè no es pot distingir
               el "no trobat" del "trobat el valor nul".
               En aquests casos, cal fer servir g_hash_table_lookup_extended
            */
            printf("found %s, age = %d, room = %d\n",
                keyPtr,
                valuePtr->age,
                valuePtr->room);
        } else
            printf("not found %s\n", keyPtr);
    }
    return 0;
}


L'execució del programa anterior segueix les mateixes indicacions que al primer exemple:

albert@atenea:~/wk-c/prova-hash-glib$  hashtables
Crea la taula
Introdueix clau age room a la taula. Per acabar d'entrar valors prem CTRL-D
albert 42 192
Afegeix albert 42 192 a la taula
Afegeix - g_hash_table_insert
montse 40 201
Afegeix montse 40 201 a la taula
Afegeix - g_hash_table_insert

^D
Busca valors a la taula. Per acabar CTRL-C
kok
Buscant kok amb g_hash_table_lookup
not found kok
albert
Buscant albert amb g_hash_table_lookup
found albert, age = 42, room = 192
montse
Buscant montse amb g_hash_table_lookup
found montse, age = 40, room = 201
^C



GDBM i TDB

En el desenvolupament de SAMBA es va veure que calia un sistema de base de dades senzill del tipus hash: una taula de claus i valors.

DBM (la BD de BSD) oferia la interfase adequada, però no permetia l'accés concurrent. Aleshores, es va reescriure DBM permetent aquest accés concurrent i el resultat va ser TDB. TDB es pot utilitzar com un alternativa concurrent de DBM.

A Ubuntu es poden instal·lar des del Centre de Programari tant la versió GNU de DBM, la GDBM, com la TDB.

TDB i GDBM ocupen molt poc espai i poden ser una bona sol·lució per a implementar taules hash amb C allà on calguin, en comptes de fer servir les funcions que ofereixen la llibreria estàndard de C, GLibc, o les de la llibreria de GTK, GLib.

Una preacució a tenir en compte és que existeixen diferents formats de fitxer dbm.

La instal·ació de TDB inclou, a més  dels llibreries i dels include,  els executables següents:

tdbbackup: és una eina que es pot fer servir per fer el backup des fitxers .tdb de Samba i verificar-ne la integritat. Si troba un fitxer .tdb de Samba fet malbé i un fitxer de backup previ, aleshores restaura el fitxer anterior.
tdbdump: és una eina que fa el volcat, o dump, d'un fitxer .tdb en un format legible. A més també pot fer el volcat d'una clau específica.           
tdbtool: permet manipular fitxers .tdb. Les opcions que ofereix aquest programa són:

albert@atenea:~$ tdbtool
tdb> help
database not open

tdbtool:
  create    dbname     : create a database
  open      dbname     : open an existing database
  transaction_start    : start a transaction
  transaction_commit   : commit a transaction
  transaction_cancel   : cancel a transaction
  erase                : erase the database
  dump                 : dump the database as strings
  keys                 : dump the database keys as strings
  hexkeys              : dump the database keys as hex values
  info                 : print summary info about the database
  insert    key  data  : insert a record
  move      key  file  : move a record to a destination tdb
  store     key  data  : store a record (replace)
  show      key        : show a record by key
  delete    key        : delete a record by key
  list                 : print the database hash table and freelist
  free                 : print the database freelist
  check                : check the integrity of an opened database
  speed                : perform speed tests on the database
  ! command            : execute system command
  1 | first            : print the first record
  n | next             : print the next record
  q | quit             : terminate
  \n                   : repeat 'next' command

per exemple, puc fer

albert@atenea:~$ tdbtool
tdb> create prova.tdb
tdb> insert clau1 valor1
tdb> insert clau2 valor2
tdb> insert clau3 valor3
tdb> dump

key 5 bytes
clau1
data 6 bytes
[000] 76 61 6C 6F 72 31                                 valor1

key 5 bytes
clau2
data 6 bytes
[000] 76 61 6C 6F 72 32                                 valor2

key 5 bytes
clau3
data 6 bytes
[000] 76 61 6C 6F 72 33                                 valor3
 tdb> quit
albert@atenea:~$ tdbdump prova.tdb
{
key(5) = "clau1"
data(6) = "valor1"
}
{
key(5) = "clau2"
data(6) = "valor2"
}
{
key(5) = "clau3"
data(6) = "valor3"
}


Amb TDB

Anem a repetir el mateix cas de prova de GLibC i GLib, però implementant-lo amb l'API C de TDB. Amb GDBM seria molt semblant.

El package de TDB em proporciona eines per a examinar fitxers de bases de dades TDB. Aquest pot ser un avantatge decisiu a l'hora de fer decantar-se per TDB per a implementar les taules hash d'una aplicació. No cal dir que l'API C de TDB permet volcar, i recuperar, taules hash a, i de, fitxers.

Com en els casos anteriors, he desenvolupat el programa amb Anjuta, amb un projecte C genèric mínim. M'ha calgut tocar el fitxer makefile.am (per a l'automake) per a afegir-li la referència a la
llibreria libtdb:


## Process this file with automake to produce Makefile.in


## Created by Anjuta


AM_CPPFLAGS = \
-DPACKAGE_DATA_DIR=\""$(datadir)"\" 


AM_CFLAGS =\
-Wall\
-g


bin_PROGRAMS = hashtables_tdb


hashtables_tdb_SOURCES = \
main.c


hashtables_tdb_LDFLAGS = -ltdb 


hashtables_tdb_LDADD = 



Vet aquí el codi (consulteu API TDB)


#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>     /* mode_t */
#include <tdb.h>


struct value {         /* aquesta estructura és la dels valors */
    int age, room;          /* emmagatzemats a la taula */
};


#define NUM_KEYS    5000    /* número d'elements. */
#define KEY_SIZE    20      /* tamanys de la clau*/


int main() {   
    TDB_CONTEXT *tdbHashTable;
    TDB_DATA tdbKey, tdbValue;
    char keysData[NUM_KEYS * KEY_SIZE]; /* espai d'emmagatzematge de la taula de claus. */
    struct value valuesData[NUM_KEYS]; /* espai d'emmagatzematge de la taula de valors. */
    char *keyPtr = keysData;     /* següent posició a l'espai de valors. */
    struct value *valuePtr = valuesData; /* següent posició a l'espai de claus. */   
    char keyToFind[KEY_SIZE]; /* clau a buscar, màxim de KEY_SIZE caràcters */
    int i=0; /* comptador de claus afegides*/


    /* Crea taula hash.*/
    printf("Crea la taula\n");
    tdbHashTable = tdb_open("prova.tdb", 
    0,
                            TDB_CLEAR_IF_FIRST, 
    O_RDWR | O_CREAT | O_TRUNC, 
    0666);


    printf("Introdueix clau age room a la taula. Per acabar d'entrar valors prem CTRL-D\n");
    while (scanf("%s %d %d", 
 keyPtr, 
 &valuePtr->age, 
 &valuePtr->room) != EOF && i++ < NUM_KEYS) {  
        /* traça */
        printf("Afegeix %s %d %d a la taula\n", 
               keyPtr, 
               valuePtr->age, 
               valuePtr->room);


        /* els posa a la taula */
        printf("Afegeix - tdb_store\n");
        
tdbKey.dptr = keyPtr;
tdbKey.dsize = strlen(keyPtr) + 1; /* caràcter 0 de final*/

tdbValue.dptr = valuePtr;
tdbValue.dsize = sizeof(struct value);

        tdb_store(tdbHashTable, tdbKey, tdbValue, TDB_INSERT);


/* prepara els punters per a nova clau valor*/
        keyPtr += strlen(keyPtr) + 1;
        valuePtr++;
    }




    /* Ara busca per la taula. */
    printf("Busca valors a la taula. Per acabar CTRL-C\n");
    keyPtr = keyToFind;
    while (scanf("%s", keyPtr) != EOF) {
        printf("Buscant %s amb tdb_fetch\n", keyPtr);
/* prepara la clau per a la cerca*/
tdbKey.dptr = keyPtr;
tdbKey.dsize = strlen(keyPtr) + 1;   
   
        tdbValue = tdb_fetch(tdbHashTable, tdbKey);
        if (tdbValue.dptr != NULL) {
            /* si ha trobat diferent de nul, el mostra. */
    /* obté el punter al valor a partir del TDB_DATA retornat*/
    valuePtr = tdbValue.dptr;


            printf("found %s, age = %d, room = %d\n",
                   keyPtr,
                   valuePtr->age,
                   valuePtr->room);
        } else
            printf("not found %s\n", keyPtr);
    }


    tdb_close(tdbHashTable);
return (0);
}


No hi han gaires diferències amb els casos anteriors. Si de cas remarcar l'ús de l'estructura TDB_DATA, per a passar, i recuperar, les claus i els valors a, i de, les funcions de l'API.

Amb la funció tdb_open es crea físicament un fitxer prova.tdb que podrem examinar (i modificar) amb tdbtool. Tanmateix, hauria pogut fer servir l'opció TDB_INTERNAL per a crear  la taula hash només en memòria.

Una execució del programa anterior:


albert@atenea:~/wk-c/prova-hash-tdb$ hashtables_tdb 
Crea la taula
Introdueix clau age room a la taula. Per acabar d'entrar valors prem CTRL-D
albert 42 125
Afegeix albert 42 125 a la taula
Afegeix - tdb_store
montse 40 125
Afegeix montse 40 125 a la taula
Afegeix - tdb_store
artiom 6 125
Afegeix artiom 6 125 a la taula
Afegeix - tdb_store
^D
Busca valors a la taula. Per acabar CTRL-C
prova
Buscant prova amb tdb_fetch
not found prova
albert
Buscant albert amb tdb_fetch
found albert, age = 42, room = 125
montse
Buscant montse amb tdb_fetch
found montse, age = 40, room = 125
artiom
Buscant artiom amb tdb_fetch
found artiom, age = 6, room = 125
^C
albert@atenea:~/wk-c/prova-hash-tdb$ 

I, a més, tenim un fitxer prova.tdb amb la taula generada. Aquest fitxer el podem examinar amb tdbtool:

albert@atenea:~/wk-c/prova-hash-tdb$ tdbtool prova.tdb
tdb> dump

key 7 bytes
artiom
data 8 bytes
[000] 06 00 00 00 7D 00 00 00                           ....}.. 

key 7 bytes
montse
data 8 bytes
[000] 28 00 00 00 7D 00 00 00                           (...}.. 

key 7 bytes
albert
data 8 bytes
[000] 2A 00 00 00 7D 00 00 00                           *...}.. 
tdb> quit
albert@atenea:~/wk-c/prova-hash-tdb$ 

Dump fa un volcat de la taula en format de cadena. Què obtenim? pèr a cada registre de la taula obtenim: la clau i 8 bytes de dades en hexadecimal. No cal dir que 7D és 125; 06 hex. és 6 decimal; 28 hex. és 42 decimal i 2A hex, 40 decimal.


Conclusió

Evidentment, aquests petits escripts només són una aproximació a la implementació de taules hash amb GlibC, Glib i TDB.

Segurament, per a la majoria d'aplicacions n'hi ha prou i són més que suficients les funcions per a hashtables de la llibreria estàndard de C (GLibC); tanmateix, en aplicacions  GTK, la GLib hi és present i aporta funcions més flexibles -per exemple, permetent definir la funció hash- i, a més, fan servir els tipus de dades de GTK, sent consistents amb la resta de l'aplicació.
Finalment, TDB pot ser una bona tria en el cas que ens interessi poder disposar de la taula hash en un fitxer i, a més, ens proporciona eines addicionals, la tdbtool, per a manipular aquests fitxers. 

dimarts, 19 de juliol del 2011

BeanShell, scripting per a Java

BeanShell (bsh) és un interpret de Java estès amb característiques de llenguatge d'scripting. BeanShell està desenvolupat en Java i s'entrega i es pot executar com aplicació standalone des del jar, però també es pot afegir com a llibreria en una aplicació standalone Java que incorporaria d'aquesta forma un llenguatge d'scripting per a fer macros, per exemple.

A tall d'exemple, BeanShell és un dels llenguatges de macros que incorpora OpenOffice.org/LibreOffice (OOo/LO). També és el llenguatge de macros de l'editor JEdit, també es pot trobar com a llenguatge de macros a l'IDE de Java NetBeans.

Encastat a una aplicació Java, i pel fet d'executar-se en la mateixa JVM de l'aplicació hoste, és capaç d'interactuar amb els objectes de l'aplicació. Com que és interpretat i no cal compilar els scripts de bsh obre una porta a l'extensió de l'aplicació hoste. I amb tota la potència de Java, doncs BeanShell és capaç d'importar packages java.

BeanShell és un jar que ocupa menys de 300KB. És considerablement més petit, doncs, que alternatives més potents com Groovy (De fet, Groovy va molt més enllà del que seria un llenguatge d'scripting. Per exemple, el framework  Grails  està basat en Groovy).

Els punts forts de BeanShell són aquests: és java, ofereix facilitats per a l'scripting, és petit, és encastable i és fa servir a OOo/LO, entre d'altres.

Punts dèbils? malgrat que la llista de correu de desenvolupadors i d'usuaris de BeanShell a SourceForge mostra certa activitat enguany, la llista d'anuncis de noves versions resta aturada des de 2005 (justament quan el JCP aprovava la creació d'una JSR per al BeanShell). El punt dèbil és, doncs, que no ha evolucionat des de fa massa temps.

En tot cas, el fet de ser un interpret de Java fa que sigui immediatament utilitzable per programadors d'aquest llenguatge.

L'escenari d'us de BeanShell seria, doncs, aquell en el que cal incorporar un llenguatge de macros a una aplicació java que sigui lleuger i, a la vegada, d'aplicació immediata.

La sintaxi del llenguatge és la de Java. Res a descobrir, doncs. Tanmateix, hi han afegides característiques orientades a l'scripting, com el "tipatge relaxat", o comandes específiques  per a entrada sortida per consola

Les referències són, per una banda el manual d'usuari; i per l'altre, el javadoc de beanshell. També és recomanable veure la presentació amb diapositives (en format .pdf) Un punt d'entrada és l'anàlisi dels exemples que també es poden trobar a la web.

Si hem instal·lat bsh des del Centre de Programari de l'Ubuntu, o si tenim instal·lat l'OpenOffice.org/LibreOffice, és possible que trobem el bsh.jar a les ubicacions següents:

/usr/share/java/bsh-2.0b4.jar
/usr/share/java/bsh.jar
/usr/lib/openoffice/basis3.2/program/classes/bsh.jar

Per engegar l'interpret n'hi ha prou amb escriure bsh a la línia de comandes. Però també el podem engegar com la aplicació Java que és:

java -jar /usr/share/java/bsh.jar bsh.

en aquest punt ja podem introduir ordres. tanmateix és més pràctic fer un script:

Fem la prova canònica. Creo l'script hello.bsh

a = "hola ";
b = " món!";
print (a + b);

i l'executo

bsh ./hello.bsh

hola  món!

Com era d'esperar... L'script anterior en bsh és indistingible d'altres llenguatges d'script que disposen de la comanda print.

Haviem dit que bsh és java amb característiques d'script. Vet aquí la primera: no ha calgut definir el tipus de les variables.

Hauria pogut ser més rigurós. Amplio l'script amb línies que són Java inequívocament:

a = "hola ";
b = " món!";
print (a + b);

String s_Nom = "Serveix TIC i Programari Lliure";
String s_llocweb = " (http://apuntstecnologia.blogspot.com/)";

System.out.println(s_Nom + ", " + s_llocweb);

System.out.println("Variable no inicialitzada: " + sNom + ", " + s_LlocWeb);

i el resultat és:

albert@atenea:~$ bsh ./hello.bsh
hola  món!
Serveix TIC i Programari Lliure,  (http://apuntstecnologia.blogspot.com/)
Variable no inicialitzada: void, void


void, en comptes de null o "". A bsh es pot verificar si una variable està inicialitzada comparant-la amb "void".

Més coses: els "scripted objects", una forma de definir objectes a l'estil de com ho fan altres llenguatges d'scripts (recorda Javascript) que aprofiten les estructures de blocs.

ClasseBsh() {
   propietat1 = 1;
   propietat2 = "un text";  
   
   Metode1() {
      print("Aquest és el mètode 1");
   }

   Metode2() {
      print("Aquest és el mètode 2");
   }

   Metode3() {
       cbsh2 = ClasseBsh2();
       print("cbsh2.propietat1: " + cbsh2.propietat1);
       print("cbsh2.propietat2: " + cbsh2.propietat2);
       cbsh2.Metode1();
       cbsh2.Metode2();
   }

   
   ClasseBsh2() {
       propietat1 = 1;
       propietat2 = "un altre text";   
       
       Metode1() {
           print("Aquest és el mètode 1 intern");
       }

       Metode2() {
           print("Aquest és el mètode 2 intern");
       }

      return this;
    }

    return this;
}

cbsh1 = ClasseBsh();

print("cbsh1.propietat1: " + cbsh1.propietat1);
print("cbsh1.propietat2: " + cbsh1.propietat2);
cbsh1.Metode1();
cbsh1.Metode2();
cbsh1.Metode3();

El resultat és:

albert@atenea:~$ bsh ./classes.bsh
cbsh1.propietat1: 1
cbsh1.propietat2: un text
Aquest és el mètode 1
Aquest és el mètode 2
cbsh2.propietat1: 1
cbsh2.propietat2: un altre text
Aquest és el mètode 1 intern
Aquest és el mètode 2 intern

Remarcar la sintaxi simplificada, i també com es poden fer classes dins de classes sense ĺímit de nivell.

Com encastar BeanShell a una aplicació Java?

Res més senzill. Podem fer una petita classe java que carrega un interpret de BeanShell que executa un script:

package com.sticipl.proves;

public class ProvaBshRun {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        new ProvaBshRun();
    }

    public ProvaBshRun() {
        try {
            // carrega script i l'executa
            System.out.println("carrega script i l'executa");
            Interpreter beanshellInterpret = new Interpreter();
            beanshellInterpret.source("/home/albert/hello.bsh"); 
      
        } catch(Exception e) {
            System.out.println("Error: " + e.toString());
        }
    }
}

 

Per a fer l'execució més senzilla, empaqueto la classe anterior en un jar. Faig servir el següent build.xml per a l'ant















<project name="provabsh" default="build" basedir=".">
  <description>
  proves amb beanshell
  </description>

  <!-- estableix propietats globals -->
  <property name="src" location="src"/>
  <property name="bin" location="bin"/>
  <property name="build" location="build"/>
  <property name="lib"  location="lib"/>


  <target name="init">
    <!-- time stap -->
    <tstamp/>
    <!-- Crea el directory bin utilitzat en la compilació -->
    <mkdir dir="${bin}"/>
  </target>


  <target name="compile" depends="init" description="compila els fitxers font" >
    <!-- Compila les fonts java de ${src} en ${build} -->
    <javac srcdir="${src}" destdir="${bin}">
      <classpath>
       <pathelement location="${lib}/bsh-2.0b4.jar"/>
      </classpath>
   </javac>
  </target>

  <target name="build" depends="compile" description="genera el jar">
    <!-- Crea el directori de distribució -->
    <mkdir dir="${build}"/>

    <jar jarfile="${build}/provabsh.jar" basedir="${bin}">
      <include name="**/*.class"/>
      <manifest>
        <attribute name="Main-Class" value="com.sticipl.proves.ProvaBshRun"/>
          <attribute name="Class-Path" value="bsh-2.0b4.jar"/>
      </manifest>
    </jar>
     
      <copy file="${lib}/bsh-2.0b4.jar" todir="${build}" />
  </target>

</project>


Amb el build.xml anterior obtinc una carpeta build on he deixat el jar provabsj.jar que conté la classe ProvaBshRun. El manifest del jar indica que cal fer servir la llibreria bsh-2.0b4.jar.

Puc executar la classe amb:

albert@atenea:~/wk-java/ProvaBsh/build$ java -jar ./provabsh.jar

o bé amb:

albert@atenea:~/wk-java/ProvaBsh/build$ java -jar -cp ./bsh-2.0b4.jar ./provabsh.jar

i el resultat de l'execució és:

carrega script i l'executa
hola  món!
Serveis TIC i Programari Lliure,  (http://apuntstecnologia.blogspot.com/)
Variable no inicialitzada: void, void
data: Tue Jul 19 18:38:08 CEST 2011


Un cop carregat l'script també es poden manipular-ne els mètodes i propietats. Per exemple, carrego l'script classes.bsh i n'executo mètodes i en canvio propietats:

package com.sticipl.proves;

import bsh.Interpreter;
import java.util.Date;

public class ProvaBshRun {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        new ProvaBshRun();
    }

    public ProvaBshRun() {
        Interpreter beanshellInterpret = new Interpreter();

        try {
            // Avaluació d'instruccions i expressions
            // Carrega script i en modifica propietats
            System.out.println("Carrega script");
            beanshellInterpret.source("/home/albert/classes.bsh");    
          
            int  iPropietat1 = ((Integer) beanshellInterpret.get("cbsh1.propietat1")).intValue();
            String sPropietat2 = (String) beanshellInterpret.get("cbsh1.propietat2");
            System.out.println("iPropietat1: " + iPropietat1);
            System.out.println("sPropietat2: " + sPropietat2);
           
           
            iPropietat1 = 25;
            beanshellInterpret.set("cbsh1.propietat1", iPropietat1);
            sPropietat2 = "aquest és el text canviat des de l'aplicació";
            beanshellInterpret.set("cbsh1.propietat2", sPropietat2);
            System.out.println("des de l'aplicació");
            System.out.println("iPropietat1: " + iPropietat1);
            System.out.println("sPropietat2: " + sPropietat2);   
            System.out.println("des de beanshell");
            beanshellInterpret.eval("print(\"cbsh1.propietat1: \" + cbsh1.propietat1);");
            beanshellInterpret.eval("print(\"cbsh1.propietat2: \" + cbsh1.propietat2);");
           
            System.out.println("Executa mètode 1:" );
            beanshellInterpret.eval("cbsh1.Metode1();");
            System.out.println("Executa mètode 2:" );
            beanshellInterpret.eval("cbsh1.Metode2();");
            System.out.println("Executa mètode 3:" );
            beanshellInterpret.eval("cbsh1.Metode3();");
        } catch(Exception e) {
            System.out.println("Error: " + e.toString());
        }
    }
}







El resultat de l'anterior és

Carrega script
cbsh1.propietat1: 1
cbsh1.propietat2: un text
Aquest és el mètode 1
Aquest és el mètode 2
cbsh2.propietat1: 1
cbsh2.propietat2: un altre text
Aquest és el mètode 1 intern
Aquest és el mètode 2 intern
iPropietat1: 1
sPropietat2: un altre text
des de l'aplicació
iPropietat1: 25
sPropietat2: aquest és el text canviat des de l'aplicació
des de beanshell
cbsh1.propietat1: 25
cbsh1.propietat2: aquest és el text canviat des de l'aplicació
Executa mètode 1:
Aquest és el mètode 1
Executa mètode 2:
Aquest és el mètode 2
Executa mètode 3:
cbsh2.propietat1: 1
cbsh2.propietat2: un altre text
Aquest és el mètode 1 intern
Aquest és el mètode 2 intern



En el jar de BeanShell es poden trobar classes per a l'execució remota d'scripts, i també per l'execució en mode servlet. Deixo per a futurs posts la revisió d'aquestes opcions.

En resum s'ha presentat l'aplicació BeanShell i les seves característiques que més immediatament es poden utilitzar en aplicacions java.