quarta-feira, 19 de outubro de 2011

A Linux trick on Oracle setup

Have you ever tried to install Oracle Database on GNU/Linux and faced the insufficient memory issue below?
Oracle Database 10g Express Edition requires 1024 MB of swap space. This system has ??? MB of swap space.
Well, I've just been caught by that when attempting to set it up on a virtual machine.
# dpkg -i oracle-xe-universal_10.2.0.1-1.1_i386.deb
(Reading database ... 125364 files and directories currently installed.)
Unpacking oracle-xe-universal (from oracle-xe-universal_10.2.0.1-1.1_i386.deb) ...
This system does not meet the minimum requirements for swap space.  Based on 
the amount of physical memory available on the system, Oracle Database 10g 
Express Edition requires 1024 MB of swap space. This system has 1019 MB 
of swap space.  Configure more swap space on the system and retry the installation.
dpkg: error processing oracle-xe-universal_10.2.0.1-1.1_i386.deb (--install):
 subprocess new pre-installation script returned error exit status 1
Errors were encountered while processing:
 oracle-xe-universal_10.2.0.1-1.1_i386.deb
Fortunately a true Operating System can handle it seamlessly! Here are the steps I followed.
Setup is claiming about just 5 megabytes... Well, let's give him it. But no more and no less! Let's create a zeroed dummy file with dd:
# dd if=/dev/zero of=5m.swp bs=1M count=5
5+0 records in
5+0 records out
5242880 bytes (5.2 MB) copied, 0.0132488 s, 396 MB/s
Here you go, an exactly 5 MB size zeroed file:
# ls -la 5m.swp 
-rw-r--r-- 1 root root 5242880 2011-10-19 10:39 5m.swp
Let's check out the available memory in the system using free:
# free
             total       used       free     shared    buffers     cached
Mem:       1026080     921612     104468          0      27036     674936
-/+ buffers/cache:     219640     806440
Swap:      1046524       2552    1043972
Alright, you need it, we'll give you:
# swapon 5m.swp
Are you satisfied now?
# free
             total       used       free     shared    buffers     cached
Mem:       1026080     921736     104344          0      27044     674936
-/+ buffers/cache:     219756     806324
Swap:      1051640       2552    1049088
Let's retry running Oracle installer:
# dpkg -i oracle-xe-universal_10.2.0.1-1.1_i386.deb
(Reading database ... 125364 files and directories currently installed.)
Unpacking oracle-xe-universal (from oracle-xe-universal_10.2.0.1-1.1_i386.deb) ...
That's it! Although that ridiculous additional 5 MB swap won't be automatically available for the next reboot, we still can tune Oracle to allocate less memory than standard settings.

quinta-feira, 13 de outubro de 2011

Générer des fichiers checksum pour Maven

Dans un répertoire Maven v2 les fichiers JAR ont l'intégrité contrôlée par des fichiers contenant des sommes MD5 et SHA1. En général ces fichiers auxiliaires sont appelés comme le nom du fichier JAR accompagnés des extensions ".md5" ou ".sha1". Par exemple, pour la librairie "jcommon-0.9.6.jar" il doit exister "jcommon-0.9.6.jar.md5" et "jcommon-0.9.6.jar.sha1".

Ces ".md5" et ".sha1" ne sont que des fichiers texte avec une chaîne string correspondant à la somme de contrôle calculée à partir de l'archive. Cette somme représente une signature unique pour chaque fichier et peut garantir qu'il ne soit pas craqué ou corrompu.

Pour générer ces fichiers au moment de la livraison d'une librairie Java, ils existent des plugins Maven pour les créer automatiquement. Néanmoins, on peut avoir le cas où des librairies JAR n'aient pas ses fichiers de contrôle (par exemple, si on a créé cette partie du répo manuellement). Cette façon, lors d'une résolution de dépendance au Maven, on va avoir des notifications telles qu'au-dessous :

[WARNING] *** CHECKSUM FAILED - Checksum failed on download

Pour illustrer ce soucis, voici un exemple d'un arbre partiel au Maven :

|-- jfree-former
|   |-- jcommon
|   |   `-- 0.9.6
|   |       |-- jcommon-0.9.6.jar
|   |       `-- jcommon-0.9.6.pom
|   `-- jfreechart
|       `-- 0.9.21
|           |-- jfreechart-0.9.21.jar
|           `-- jfreechart-0.9.21.pom

Il faut d'abord créer un script Shell appelée checksum.sh avec le contenu ci-dessous :

#!/bin/bash

if [ $# -ne 2 ]
then
 echo "Usage : checksum [md5|sha1] <file-name>"
 echo "Sample: checksum sha1 /tmp/dir/myfile.jar"
 exit 1
fi

format=$1
file="$2"

if [ ! -f $file ]
then
 echo "File not found: $file"
 exit 2
fi

if [ "$format" == "md5" -o "$format" == "sha1" ]
then
 ${format}sum "$file" | cut -d' ' -f1 | tr -d "\n" > "$file.$format"
else
 echo "Please choose a format: md5 or sha1"
 exit 2
fi

echo "Created checksum file $file.$format"

Ensuite, on doit créer un autre script nommé generate-checksums.sh contenant ces lignes :

#!/bin/bash

EXTENSIONS="jar pom"
ALGORITHMS="sha1 md5"

PROGDIR=`dirname $0`
export PATH="$PATH:$PROGDIR"

for ext in $EXTENSIONS
do
 for alg in $ALGORITHMS
 do
  find -type f -name "*.$ext" -exec checksum.sh $alg {} \;
 done
done

N'oubliez pas de donner des permissions d'exécution à ses fichiers .sh, en roulant le commande chmod +x *.sh.

Maintenant, il faut seulement aller au répertoire désiré et ensuite exécuter le script generate-checksums.sh. Voyez :

$ cd /var/maven/repo/

$ /home/user/scripts/generate-checksums.sh
Created checksum file ./jfree-former/jcommon/0.9.6/jcommon-0.9.6.jar.sha1
Created checksum file ./jfree-former/jfreechart/0.9.21/jfreechart-0.9.21.jar.sha1
Created checksum file ./jfree-former/jcommon/0.9.6/jcommon-0.9.6.jar.md5
Created checksum file ./jfree-former/jfreechart/0.9.21/jfreechart-0.9.21.jar.md5
Created checksum file ./jfree-former/jcommon/0.9.6/jcommon-0.9.6.pom.sha1
Created checksum file ./jfree-former/jfreechart/0.9.21/jfreechart-0.9.21.pom.sha1
Created checksum file ./jfree-former/jcommon/0.9.6/jcommon-0.9.6.pom.md5
Created checksum file ./jfree-former/jfreechart/0.9.21/jfreechart-0.9.21.pom.md5

Et voici le résultat pour le cas d'exemple :

|-- jfree-former
|   |-- jcommon
|   |   `-- 0.9.6
|   |       |-- jcommon-0.9.6.jar
|   |       |-- jcommon-0.9.6.jar.md5
|   |       |-- jcommon-0.9.6.jar.sha1
|   |       |-- jcommon-0.9.6.pom
|   |       |-- jcommon-0.9.6.pom.md5
|   |       `-- jcommon-0.9.6.pom.sha1
|   `-- jfreechart
|       `-- 0.9.21
|           |-- jfreechart-0.9.21.jar
|           |-- jfreechart-0.9.21.jar.md5
|           |-- jfreechart-0.9.21.jar.sha1
|           |-- jfreechart-0.9.21.pom
|           |-- jfreechart-0.9.21.pom.md5
|           `-- jfreechart-0.9.21.pom.sha1

Voilà ! Désormais votre répertoire Maven contient des informations de vérification pour les fichiers et la résolution de dépendances ne lancera plus le message "CHECKSUM FAILED".

Si vous voulez, le code source complet peut être obtenu dans cette adresse : https://gitorious.org/shell-scripts/checksum

terça-feira, 4 de outubro de 2011

Mantendo atualizado o índice FTS no PostgreSQL



Dando sequência ao post anterior "Full Text Search em português no PostgreSQL", veremos como manter atualizado o índice de busca textual, particularmente no caso de os dados da tabela sofrerem muitas atualizações (i.e., inclusões e modificações).


Só para relembrar, o diagrama a seguir ilustra o funcionamento do mecanismo de Full Text Search no PostgreSQL, em que fazem parte processos como parser, normalizador e indexador:


Para exemplificar o problema apresentado no início desse post, considere que a tabela MUNICIPIOS esteja devidamente populada, tendo a coluna de suporte para a busca textual criada e o índice do tipo GIN (ou GiST) associado. Sendo assim, podemos fazer consultas SQL desse tipo:

SELECT nome, uf FROM municipios
WHERE busca @@ plainto_tsquery(simples('agua lindoia'));

Veja o resultado:

curso=# SELECT nome, uf FROM municipios
curso-# WHERE busca @@ plainto_tsquery(simples('agua lindoia'))
       nome       | uf 
------------------+----
 Águas de Lindóia | SP
(1 registro)

O que acontece agora se incluirmos um novo registro na tabela? Veja:

INSERT INTO municipios (codigo, nome, uf)
VALUES (100, 'Águas de Lindóia do Sul', 'RS');

Ao executarmos a busca anterior, o município recém-incluído não aparecerá... Isso porque a coluna de suporte à busca não foi atualizada. Veja o conteúdo dela:

curso=# SELECT nome, uf, busca FROM municipios WHERE codigo = 100;
          nome           | uf | busca 
-------------------------+----+-------
 Águas de Lindóia do Sul | RS | 
(1 registro)

Para manter essa coluna atualizada automaticamente em operações de INSERT ou UPDATE, precisamos de um disparador (trigger). No PostgreSQL criamos uma função em linguagem procedural (geralmente em PL/pgSQL) e em seguida a associamos a um disparador.

Sendo assim, crie a função de trigger com o código abaixo:

CREATE FUNCTION municipios_trigger()
RETURNS trigger AS $$
begin
  new.busca := to_tsvector(simples(new.nome));
  return new;
end
$$ LANGUAGE plpgsql;

Ao ser chamada, ela fará com que o conteúdo da coluna "busca" seja preenchido com o texto gerado da normalização da coluna "nome".

Em seguida, crie o disparador de atualização:

CREATE TRIGGER municipios_tsupdate
BEFORE INSERT OR UPDATE ON municipios
FOR EACH ROW EXECUTE PROCEDURE municipios_trigger();

Ou seja, antes de cada INSERT ou UPDATE na tabela, a função municipios_trigger() será invocada para preencher automaticamente a coluna "busca".

Agora experimente modificar aquele registro inserido acima:

UPDATE municipios SET uf = uf WHERE codigo = 100;

Observe como ficou o conteúdo daquela linha:

curso=# SELECT nome, uf, busca FROM municipios WHERE codigo = 100;
          nome           | uf |           busca           
-------------------------+----+---------------------------
 Águas de Lindóia do Sul | RS | 'agu':1 'lindo':3 'sul':5
(1 registro)

Finalmente efetue a busca textual que havia falhado:

curso=# SELECT nome, uf FROM municipios
curso-# WHERE busca @@ plainto_tsquery(simples('agua lindoia'));
          nome           | uf 
-------------------------+----
 Águas de Lindóia do Sul | RS
 Águas de Lindóia        | SP
(2 registros)

Tente agora fazer uma modificação no nome desse município:

UPDATE municipios SET nome = 'Águas Quentes do Sul' WHERE codigo = 100;

E então refaça a busca considerando o novo nome:

curso=# SELECT codigo, nome, uf FROM municipios
WHERE busca @@ plainto_tsquery(simples('agua quente'));
 codigo |         nome         | uf 
--------+----------------------+----
    100 | Águas Quentes do Sul | RS
(1 registro)


Índice sempre up-to-date! Muito fácil, né? :D

Referências


[1] PostgreSQL 8.3 Documentation - Full Text Search