Category Archives: Documentation

Installer l’IDE Arduino 1.5 sous Ubuntu 13.10

Je reprends mon article précédent sur l’installation de l’IDE Arduino sous Ubuntu, mais cette fois on va installer la version qui est encore en béta : Arduino 1.5 et sous Ubuntu 13.10. Nous allons toujours connecter une carte Arduino UNO via le cable USB.

L’IDE 1.5 ajoute le support de nouvelles architectures. Dans mon cas, cela devrait m’aider à essayer de programmer directement la partie arduino de ma nouvelle carte Udoo. Cette carte est une sacrée bestiole : un raspberry PI et une arduino due en une seule carte. Mais j’en parlerai une autre fois.

On va donc tout refaire comme la fois précédente… ou presque.

Étape 1, vérifier la présence de Java

Ouvrir un terminal et taper un classique :

~$ java -version
java version "1.7.0_51"
OpenJDK Runtime Environment (IcedTea 2.4.4) (7u51-2.4.4-0ubuntu0.13.10.1)
OpenJDK 64-Bit Server VM (build 24.45-b08, mixed mode)

Tout est donc parfait, sinon il faut installer un OpenJDK 6 ou 7 (avec un “aptitude install openjdk-7-jdk” par exemple).

Étape 2,  installer les dépendances pour l’IDE

Dans le tutoriel précédent, j’indiquais qu’il fallait installer les dépendances suivantes : gcc-avr et avr-libc. Cependant, il semble que cela ne soit plus nécessaire depuis la version 1.0.1 de l’IDE arduino car il y a une version packagée avec l’IDE. Je n’ai donc pas réalisé cette étape et je confirme : cela fonctionne très bien sans.

Étape 3, ajouter l’utilisateur au group dialout

Pour pouvoir connecter la carte arduino au port USB de l’ordinateur et pour pouvoir communiquer avec, il faut que l’utilisateur qui va lancer l’IDE appartienne au groupe dialout. En considérant que l’utilisateur s’appelle marc :

~$ sudo usermod -aG dialout marc

Note : le nouveau groupe n’est pas affecté à la session courante. Vous pouvez tester une commande id ou groups dans un terminal. Pour que cela prenne effet, il faut soit redémarrer la session utilisateur, soit faire un su - marc (remplacez marc par votre utilisateur bien entendu) dans un terminal et lancer l’IDE depuis ce terminal.

Si vous n’ajoutez pas l’utilisateur au groupe dialout — ou si l’affectation n’est pas prise en compte car vous n’avez pas redémarré votre session — vous aurez ce genre de message d’erreur lorsque vous voudrez charger un sketch sur l’arduino :

avrdude: ser_open(): can't open device "/dev/ttyACM0": Permission denied
ioctl("TIOCMGET"): Inappropriate ioctl for device

Mais cela ne devrait pas vous arriver, car je vous ai bien prévenu quand même, non ?

Étape 4, installer l’IDE Arduino

Aller sur la page de téléchargement d’Arduino et récupérer la dernière version 1.5 qui correspond à votre architecture, 32 bits ou 64 bits. Ici, ce sera la 64 bits. Vous devez savoir où vous voulez l’installer. Chez moi ce sera dans $HOME/arduino.

~$ mkdir ~/arduino
~$ cd ~/Downloads
~/Downloads$ tar -C ~/arduino -xvzf arduino-1.5.6-r2-linux64.tgz
~/Downloads$ cd ~/arduino/arduino-1.5.6-r2/
~/arduino/arduino-1.5.6-r2$

Étape 5, connecter la carte et lancer l’IDE

Connecter la carte. Elle doit s’alimenter et les leds clignoter. Pour vérifier que le port correspondant est bien créé, on peut faire un ls sur le tty correspondant :

~/arduino/arduino-1.5.6-r2$ ls -l /dev/ttyACM*
crw-rw---- 1 root dialout 166, 0 févr. 24 22:42 /dev/ttyACM0

On trouve bien le /dev/ttyACM0 et on voit bien qu'il appartient au groupe dialout.

Maintenant ouvrons l’IDE, depuis le dossier contenant l’extraction de l’archive, lancer la commande arduino :

~/arduino/arduino-1.5.6-r2$ ./arduino

Cela ouvre une fenêtre comme celle de la capture suivante, où l’on voit que l’IDE n’est pas configuré sur le bon port de communication, il prend par défaut COM1 alors qu’on veut /dev/ttyACM0  :

L'ide Arduino 1.5.6 au premier lancementPour modifier le port, dans le menu “Tools > Serial Port” choisir celui qui vous intéresse. Dans mon cas, ce sera /dev/ttyACM0.

Changement de port de communication pour l'arduinoCe paramétrage sera conservé au prochain démarrage de l’IDE.

Et voilà, vous pouvez maintenant charger des sketchs sur votre carte arduino… Vous pouvez commencer par l’exemple blink fourni avec l’IDE, il fait clignoter la LED intégrée à la carte UNO :

Charger l'exemple "blink" intégré à l'IDE arduino

Puis le déployer directement :

Déployer le sketch "blink" sur l'arduino connectée

Bonne suite.

Micro benchmark de convertisseurs de tableaux de bytes vers représentation Hexa avec Java

Contexte

Il y a quelques temps maintenant je suis tombé sur une offre d’emploi de la société ARCA Computing. Cette annonce est intéressante dans sa présentation, car pour pouvoir candidater il fallait résoudre une petite énigme qui consistait à récupérer un projet hébergé sur github, l’exécuter en remplissant quelques trous pour obtenir le contenu de l’annonce depuis le site web d’ARCA.

Bon, j’avoue, j’ai triché car j’ai juste récupéré le bout de code intéressant pour obtenir l’url, mis cela dans mon IDE pour obtenir le lien que j’ai ouvert directement dans un navigateur. Mais bref, là n’est pas le sujet…

Ce qui a piqué ma curiosité, c’est la méthode convertToHex du petit exercice. J’avais souvenir d’avoir déjà utilisé ce genre de méthode, mais pas avec cette implémentation. Je me suis donc demandé si elle était plus intéressante que d’autres qu’on peut trouver ici et là. Après une petite recherche, je suis tombé sur un message de Stack Overflow qui proposait plusieurs implémentations : http://stackoverflow.com/questions/9655181/convert-from-byte-array-to-hex-string-in-java. Cela permettait d’avoir une bonne liste pour faire un petit comparatif à l’arrache.

Le principe est simple :

  • Je fixe une limite de temps d’exécution : EXCLUSION_DELAY (en ms).
  • J’itère sur les convertisseurs en les exécutants LOOPS fois sur une chaine d’entrée qui augmente à chaque itération. La chaine d’entrée est construite par répétition d’une chaine de base autant de fois que le numéro d’itération courante.
  • Lorsqu’un convertisseur dépasse EXCLUSION_DELAY ms pour réaliser ses LOOPS conversions sur la chaine d’entrée, alors il est exclu de la suite du test et j’enregistre le numéro d’itération où il a échoué.
  • Quand tous les convertisseurs sont finalement exclus, le test est terminé. J’affiche donc le résultat c’est à dire le numéro d’itération correspondant à l’exclusion du convertisseur.

Ci-dessous les différentes implémentations que j’ai collectées. J’ai tout placé dans une seule classe pour te faciliter la vie lecteur : si tu veux reproduire, pas besoin de télécharger une archive et tout le bataclan. Créer une nouvelle classe et copier/coller le code qui suit te permettra de tester rapidement et simplement de ton côté. Le “Converter 0” est l’implémentation extraite du code de l’annonce. Les “Converter 1“, “Converter 2“, “Converter 3“, “Converter 5“, “Converter 6“, “Converter 7” (il s’agit d’une légère variante du 6 qui n’apporte rien, j’aurai dû le supprimer),  et “Converter 8” sont extraits de l’article de Stack Overflow ou des articles pointés. Le “Converter 4” est un mélange que j’ai réalisé entre les “Converter 3” et “Converter 5

Le code

package net.ozim;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author <a href="mailto:dev@arliguy.net">Bruno ARLIGUY</a>
 */
public class HexaString {

    private interface Converter
    {
        public String execute(final byte[] data);
        public String getName();

        public boolean isExcluded();
        public boolean excluded();
    }

    private static abstract class AbstractConverter implements Converter {
        private int _count = 0;

        @Override
        public boolean isExcluded() {
            return _count > 2;
        }

        @Override
        public boolean excluded() {
            _count ++;

            return this.isExcluded();
        }
    }

    private static class Converter0 extends AbstractConverter {
        @Override
        public final String execute(final byte[] data) {
            final StringBuilder sb = new StringBuilder(2 * data.length);
            for (int i = 0; i < data.length; i++) {
                int halfbyte = (data[i] >>> 4) & 0x0F;
                int two_halfs = 0;
                do {
                    if ((0 <= halfbyte) && (halfbyte <= 9)) {
                        sb.append((char) ('0' + halfbyte));
                    }
                    else {
                        sb.append((char) ('a' + (halfbyte - 10)));
                    }
                    halfbyte = data[i] & 0x0F;
                } while (two_halfs++ < 1);
            }
            return sb.toString();
        }

        @Override
        public final String getName() {
            return "Converter 0";
        }
    }

    private static class Converter1 extends AbstractConverter {
        @Override
        public final String execute(final byte[] data) {
            final StringBuilder sb = new StringBuilder(2 * data.length);
            for(byte b: data) {
                sb.append(String.format("%02x", b&0xff));
            }
            return sb.toString();
        }

        @Override
        public final String getName() {
            return "Converter 1";
        }
    }

    private static class Converter2 extends AbstractConverter {
        @Override
        public final String execute(final byte[] data) {
            final StringBuilder sb = new StringBuilder(2 * data.length);
            for(byte b: data) {
                sb.append(Integer.toString( ( b & 0xff ) + 0x100, 16).substring( 1 ));
            }

            return sb.toString();
        }

        @Override
        public final String getName() {
            return "Converter 2";
        }
    }

    private static class Converter3 extends AbstractConverter {
        private static final String _chars = "0123456789abcdef";

        @Override
        public final String execute(final byte[] data) {
            final StringBuilder sb = new StringBuilder(2 * data.length);
            for (final byte b : data) {
                sb.append(_chars.charAt((b & 0xF0) >>> 4)).append(_chars.charAt((b & 0x0F)));
            }
            return sb.toString();
        }

        @Override
        public final String getName() {
            return "Converter 3";
        }
    }

    /**
     * Un mélange du converter3 et converter5
     */
    private static class Converter4 extends AbstractConverter {
        private static final String _chars = "0123456789abcdef";

        @Override
        public final String execute(final byte[] data) {
            final char[] result = new char[2 * data.length];
            int index = 0;

            for (final byte b : data) {
              result[index++] = _chars.charAt((b & 0xF0) >>> 4);
              result[index++] = _chars.charAt((b & 0x0F));
            }
            return new String(result);
        }

        @Override
        public final String getName() {
            return "Converter 4";
        }
    }

    private static class Converter5 extends AbstractConverter {
        private static final byte[] _chars = {
            (byte)'0', (byte)'1', (byte)'2', (byte)'3',
            (byte)'4', (byte)'5', (byte)'6', (byte)'7',
            (byte)'8', (byte)'9', (byte)'a', (byte)'b',
            (byte)'c', (byte)'d', (byte)'e', (byte)'f'
        };

        @Override
        public final String execute(final byte[] data) {
            final byte[] hex = new byte[2 * data.length];
            int index = 0;

            for (byte b : data) {
              final int v = b & 0xFF;
              hex[index++] = _chars[v >>> 4];
              hex[index++] = _chars[v & 0xF];
            }
            return new String(hex, StandardCharsets.US_ASCII);
        }

        @Override
        public final String getName() {
            return "Converter 5";
        }
    }

    private static class Converter6 extends AbstractConverter {
        private static final char[] _chars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

        @Override
        public final String execute(final byte[] data) {
            final char[] hexChars = new char[data.length * 2];
            int v;
            for (int j = 0; j < data.length; j++) {
                v = data[j] & 0xFF;
                hexChars[j * 2] = _chars[v >>> 4];
                hexChars[j * 2 + 1] = _chars[v & 0x0F];
            }
            return new String(hexChars);
        }

        @Override
        public final String getName() {
            return "Converter 6";
        }
    }

    private static class Converter7 extends AbstractConverter {
        private static final String _chars = "0123456789abcdef";

        @Override
        public final String execute(final byte[] data) {
            final char[] hexChars = new char[data.length * 2];
            int v;
            for (int j = 0; j < data.length; j++) {
                v = data[j] & 0xFF;
                hexChars[j * 2] = _chars.charAt(v >>> 4);
                hexChars[j * 2 + 1] = _chars.charAt(v & 0x0F);
            }
            return new String(hexChars);
        }

        @Override
        public final String getName() {
            return "Converter 7";
        }
    }

    /**
     * Note : Using this will remove the leading zeros
     */
    private static class Converter8 extends AbstractConverter {
        @Override
        public final String execute(final byte[] data) {
            return new BigInteger(1, data).toString(16);
        }

        @Override
        public final String getName() {
            return "Converter 8";
        }
    }

    private static class ExclusionStep {
        private final Converter _converter;
        private final int       _step;

        public ExclusionStep(final Converter converter, final int step) {
            _converter = converter;
            _step = step;
        }

        @Override
        public String toString() {
            return String.format("%s at step %4d", _converter.getName(), _step);
        }
    }

    private static long doTest(final Converter c, final byte[] data, final int loops) {
        final long start = System.currentTimeMillis();

        for (int i = loops; i > 0; i--) {
            c.execute(data);
        }

        final long stop = System.currentTimeMillis();

        return (stop - start);
    }

    private final static int LOOPS = 40_000;
    private final static int EXCLUSION_DELAY = 300;

    public static void main(final String[] args) throws NoSuchAlgorithmException {
        final String base = "€€ ma base à mettre en hex string… ";
        final List<Converter> converters = new ArrayList<>();

        converters.add(new Converter0());
        converters.add(new Converter1());
        converters.add(new Converter2());
        converters.add(new Converter3());
        converters.add(new Converter4());
        converters.add(new Converter5());
        converters.add(new Converter6());
        converters.add(new Converter7());
        converters.add(new Converter8());

        final byte[] validation = base.getBytes(StandardCharsets.UTF_8);

        for (Converter c : converters) {
            System.out.println(String.format("validation %s : %s", c.getName(), c.execute(validation)));
        }

        //warmup
        for (Converter c : converters) {
            doTest(c, validation, LOOPS);
        }

        boolean stop;
        int count = 0;
        final List<ExclusionStep> exclusions = new ArrayList<>();
        do {
            //create content to convert, longer at each loop
            final String current = new String(new char[++count]).replace("\0", base);
            final byte[] data = current.getBytes(StandardCharsets.UTF_8);

            System.out.println(String.format("step %d", count));

            int countExcluded = 0;
            for (Converter c : converters) {
                if (c.isExcluded()) {
                    countExcluded ++;
                }
                else {
                    final long duration = doTest(c, data, LOOPS);
                    if (duration > EXCLUSION_DELAY && c.excluded()) {
                        exclusions.add(new ExclusionStep(c, count));
                        System.out.println(String.format("Excluding %s", c.getName()));
                    }
                }
            }

            stop = countExcluded == converters.size();

        } while (! stop);

        for (ExclusionStep exclusion : exclusions) {
            System.out.println(exclusion.toString());
        }
    }
}

Le résultat

Voici ce que j’obtiens sur ma machine :

Converter 1 at step    3
Converter 8 at step    5
Converter 2 at step    6
Converter 3 at step   39
Converter 0 at step   42
Converter 5 at step   78
Converter 4 at step  111
Converter 7 at step  112
Converter 6 at step  113

NB : Attention, cela ne représente que le résultat d’une exécution sur ma machine, il n’est pas garanti qu’ailleurs le résultat soit identique.

Il ressort donc que le “Converter 0” est pile-poile au milieu de la liste. Que le “Converter 4” que j’ai fait en mixant le 3 et le 5 se comporte bien mieux qu’eux et qu’il égale même le plus rapide qui est le “Converter 6” (le 7 étant une variante inutile).

Que conclure de ce petit test ? Que si vous avez besoin de faire plus de 40 000 conversions de tableaux de bytes en représentation hexa en moins de 300 ms sur de gros tableaux de bytes, alors il faut faire attention à l’algo utilisé. Sinon … Il y a peut être une meilleure façon de passer le temps 🙂

Et je me dis maintenant qu’il faudrait vraiment que j’essaye un framework de micro-benchmarks réalisé par des gens plus compétents que moi pour voir ce que cela donne. J’ai noté comme outils :

 

Proxy socks via SSH pour Firefox

En milieu hostile — victime de censure ou plus bêtement connecté depuis un wifi public, un hôtel, etc. — ou par volonté de ne pas trop laisser les sites consultés vous suivre géographiquement, il est préférable de pouvoir utiliser un proxy. Dans le cas qui nous intéresse, nous allons utiliser un tunnel SSH en tant que proxy SOCKS local pour le navigateur Mozilla Firefox. C’est super simple, il suffit d’avoir un compte SSH qui traîne. Pour la suite de l’exercice, ce compte SSH sera nommé “tunnel“.

Ouvrir le tunnel

Pour cela, rien de plus simple, tu prends ta ligne de commande et tu tapes :

~$ ssh -2NfCT -D 8080 tunnel@example.org

Avec cette commande tu tu connectes en tant qu’utilisateur ‘tunnel‘ sur la machine ‘example.org‘ et le tunnel est accessible en local sur le port 8080. Pour vérifier que le tunnel soit bien là :

~$ netstat -apnt

(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
[…]
tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      -               
tcp        0      0 127.0.0.1:8080          0.0.0.0:*               LISTEN      4464/ssh
[…]

Nous sommes content, il y a bien SSH sur le port 8080.

Vérifier notre IP

Avant d’utiliser le proxy, nous allons vérifier notre adresse IP publique. Car une fois que le navigateur sera configuré pour utiliser le proxy, elle devra être différente et correspondre à l’adresse IP du serveur SSH utilisé.

Il existe une foule de service pour découvrir son adresse IP, mais je vais utiliser le mien : ipso.me. J’obtiens donc :

IPso.me découvre mon adresse IP - Avant proxy

Configurer Firefox pour utiliser le proxy SOCKS

Dans Firefox, ouvrir le menu “Edit > Preferences” et dans la fenêtre de dialogue choisir l’onglet “Advanced” puis “Network” et finalement le bouton “Settings” :

Firefox Preferences

Choisir l’option “Manual proxy configuration”, cela active toute une série de champs de saisies. Dans le champ “SOCKS Host” y mettre “127.0.0.1”, notre adresse de loopback (à adapter selon configuration) et dans le champ “Port” y mettre “8080” qui est le port d’écoute du tunnel. Ensuite bien s’assurer que “SOCKS v5” est sélectionné, ainsi :

Connection Settings

Tu penses en avoir fini, mais non, il manque le dernier détail qui tue : proxifier les requêtes DNS. En effet, sur Firefox elles ne passent pas par défaut via le proxy SOCKS. Il parait que d’autres navigateurs le font. Mais pour Firefox, il faut lui forcer la main. Pour cela, ouvrir (1) “about:config” (saisir “about:config” dans la barre d’adresse) puis rechercher (2) l’entrée “network.proxy.socks_remote_dns” qui est à “false” par défaut. Double cliquer (3) sur la ligne correspondante pour passer la valeur à “true” :

about:config - Mozilla Firefox

 

Vérifier

Tu retournes sur ton site préféré pour découvrir ton adresse IP et voilà :

IPso.me découvre mon adresse IP - Après proxy

Tout est pour le mieux dans le meilleur des mondes.

Note : Il n’y a pas que Firefox dans la vie. Pour lancer Chromium en mode incognito et pour qu’il utilise le proxy SOCKS sans fuite DNS, d’après cette page du site chromium.org il faut faire ainsi :

~$ chromium-browser --incognito --proxy-server="socks5://127.0.0.1:8080" --host-resolver-rules="MAP * 0.0.0.0"

 

Installer l’IDE Arduino sous Ubuntu 12.10

Note du 24/02/2014 : j’ai publié une mise-à-jour de cet article pour installer la version 1.5 béta de l’IDE sous ubuntu 13.10.

Voici comment j’ai installé l’IDE Arduino sous mon Ubuntu 12.10 sans passer par le package fourni avec la distribution : cela permet d’avoir la dernière version car celle des dépôts Ubuntu est plus ancienne que celle qu’on peut trouver sur le site Arduino.

Étape 1, vérifier la présence de Java

Ouvrir un terminal et taper un classique :

~$ java -version
java version "1.7.0_09"
OpenJDK Runtime Environment (IcedTea7 2.3.4) (7u9-2.3.4-0ubuntu1.12.10.1)
OpenJDK 64-Bit Server VM (build 23.2-b09, mixed mode)

Tout est donc parfait, sinon il faut installer un OpenJDK 6 ou 7.

Étape 2,  installer les dépendances pour l’IDE

Les dépendances nécessaires en plus de Java sont les suivantes : gcc-avr et avr-libc. Leur installation va automatiquement ajouter une série de dépendances si elles ne sont pas déjà présentes, comme par exemple binutils-avr.

~$ sudo apt-get install gcc-avr avr-libc
[…]
Setting up binutils-avr (2.20.1-3) ...
Setting up gcc-avr (1:4.7.0-2) ...
Setting up avr-libc (1:1.8.0-2) ...

Tout s’est bien passé jusqu’à présent.

Étape 3, ajouter l’utilisateur au group dialout

Pour pouvoir connecter la carte arduino au port USB de l’ordi et pour pouvoir communiquer avec, il faut que l’utilisateur qui va lancer l’ide appartienne au groupe dialout. En considérant que l’utilisateur s’appelle marc :

~$ sudo usermod -aG dialout marc

Étape 4, installer l’IDE Arduino

Aller sur la page de téléchargement d’Arduino et récupérer la version qui correspond à votre architecture, 32 bits ou 64 bits. Ici, ce sera la 64 bits. Vous devez savoir où vous voulez l’installer. Chez moi ce sera dans $HOME/arduino.

~$ mkdir ~/arduino
~$ cd ~/Downloads
~/Downloads$ tar -C ~/arduino -xvzf arduino-1.0.3-linux64.tgz
~/Downloads$ cd ~/arduino/arduino-1.0.3/
~/arduino/arduino-1.0.3$

Étape 5, connecter la carte et lancer l’ide

Connecter la carte. Elle doit s’alimenter et les leds clignoter. Pour vérifier que le port correspondant est bien créé, on peut faire un ls sur le tty correspondant :

~/arduino/arduino-1.0.3$ ls -l /dev/ttyACM*
crw-rw---- 1 root dialout 166, 0 févr. 12 22:45 /dev/ttyACM0

On trouve bien le /dev/ttyACM0.

Maintenant ouvrons l’IDE, depuis le dossier contenant l’extraction de l’archive, lancer la commande arduino :

~/arduino/arduino-1.0.3$ ./arduino

Cela ouvre une fenêtre comme celle de la capture suivante, où l’on voit que l’IDE n’est pas configuré sur le bon port de communication, il prend par défaut COM1 alors qu’on veut /dev/ttyACM0  :

L'ide Arduino 1.0.3 au premier lancement

Pour modifier le port, dans le menu “Tools > Serial Port” choisir celui qui vous intéresse. Dans mon cas, ce sera /dev/ttyACM0.

Changement de port de communication pour l'arduino

Et voilà, vous pouvez maintenant uploader des sketchs sur votre carte arduino…

Un montage fait en se basant du guide présent dans le Starter Kit d'Arduino
Un montage fait en se basant du guide présent dans le Starter Kit d’Arduino

Bonne suite.

WordPress NextGEN : navigation avec les flèches de direction

Sous WordPress, avec une galerie NextGEN qui utilise l’effet thickbox, pour faire défiler les images à l’aide des touches directionnelles du clavier (les flèches gauche et droite il faut procéder comme suit :

Dans le fichier “wp-includes/js/thickbox/thickbox.js“, trouver ce bloc de code :

if(keycode == 27){ // close
 tb_remove();
} else if(keycode == 190){ // display previous image
 if(!(TB_NextHTML == "")){
  document.onkeydown = "";
  goNext();
 }
} else if(keycode == 188){ // display next image
 if(!(TB_PrevHTML == "")){
  document.onkeydown = "";
  goPrev();
 }
}

Et remplacer le keycode :

  • 190 par 39
  • 188 par 37

Attention : comme ce fichier fait partie de la distribution WordPress, à chaque mise-à-jour il faut refaire la modification.

Serveur Exim – commandes de bases

Voici quelques commandes à utiliser avec un serveur de mail Exim. Les serveurs que j’utilise sont souvent sous Ubuntu Linux.

Voir le fichier de log d’exim

Les logs sont situés ici :

/var/log/exim4/mainlog

Voir les mails qui sont en attente dans la file d’attente

Utiliser la commande suivante pour voir les mails qui sont encore dans la file d’attente et donc en attente d’être expédiés :

%> mailq

Forcer le traitement de la file d’attente

Pour forcer exim à traiter la file d’attente et donc à essayer de délivrer les mails :

%> exim4 -qff

Relancer la configuration d’exim

%> dpkg-reconfigure exim4-config

Tomcat et Oracle sur un OVH mCloud

Préparation

Dans cet exemple, nous nous basons sur une installation avec un Ubuntu Server 10.04 LTS sur un mC OVH. Nous allons installer Tomcat 6.0, Oracle XE (10g Express Edition) et MySQL. Ainsi nous aurons une jolie VM pour tester plein de scénarios possibles : parfait pour mettre rapidement en ligne des développements ou faire des environnements de tests jetables : la facturation à l’heure du mC nous offre bcp de souplesse.

Dans un premier temps nous allons mettre à jour la distribution et faire quelques paramétrages de base.  Ensuite nous nous attaquerons à l’installation des différents logiciels.

Configurer SSH sur poste client

Pour se connecter à un mC par ssh il faut utiliser une clef qui est générée par l’interface d’administration. Nommons cette clef key.pem. Pour pouvoir l’utiliser pour se connecter à un mC, voici la procédure :

1 placer la clef dans le dossier $HOME/.ssh

2 modifier les droits sur cette clef :

%> chmod 600 key.pem

3 utiliser ssh en fournissant le chemin de la clef :

%> ssh -i $HOME/.ssh/key.pem root@mc-178-...

Pour ne pas se compliquer la vie à indiquer chaque fois le chemin vers la clef à utiliser, il faut indiquer dans le fichier $HOME/.ssh/config quelle clef utiliser pour quel host. Faire “man ssh_config” pour plus d’informations. Voici comment procéder :

%> vi $HOME/.ssh/config

Host mc-178-…
IdentityFile ~/.ssh/key.pem

En répétant le bloc Host/IdentityFile autant de fois que vous avez de mC. Attention cependant, quand le mC est archivé puis redémarré depuis l’interface il change de nom, il faudra donc mettre à jour ce fichier de configuration.

Mettre à jour la distribution

%> aptitude update
%> aptitude upgrade

Installer rcconf

%> aptitude install rcconf

Dé-activer bind9

Lancer rcconf et décocher bind9 dans la liste. mdadm aussi, il n’y a pas de raid sur le cloud. La liste de ce qui est supprimable n’est pas exhaustive mais je n’ai pas cherché plus.

Installer un JDK

Rien de plus simple, on passe par aptitude :

%> aptitude install openjdk-6-jdk

Ensuite vérifier si il est bien installé :

%> java -version

java version “1.6.0_18”

OpenJDK Runtime Environment (IcedTea6 1.8) (6b18-1.8-4ubuntu3)

OpenJDK Server VM (build 16.0-b13, mixed mode)

Installer Tomcat 6.0

%> aptitude install tomcat6 tomcat6-admin libtcnative-1

L’installation de libtcnative-1 nous permet d’avoir Tomcat qui utilise APR. Nous installons aussi tomcat6-admin pour avoir les applications d’administration de Tomcat. Elles nous seront utiles pour déployer de nouveaux contextes. Il nous faut ensuite installer les API JavaMail et JavaBeans Activation Framework (JAF) :

%> aptitude install libgnujaf-java libgnumail-java

Ajouter ces bibliothèques dans celles chargées par Tomcat.

%> ln -s /usr/share/java/gnumail.jar /usr/share/tomcat6/lib/
%> ln -s /usr/share/java/activation.jar /usr/share/tomcat6/lib/

Il est possible de modifier les paramètres utilisés par la jvm qui lancera tomcat en éditant le fichier /etc/default/tomcat6.  Par exemple :

JAVA_OPTS=”${JAVA_OPTS} -XX:+UseConcMarkSweepGC -XX:+CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabled -Xms64m -Xmx256m -Dfile.encoding=UTF8 -XX:MaxPermSize=128m”

Installer Oracle XE

Ajouter la source oracle à la liste des sources (inspiré de la doc ubuntu-fr) :

%> vi /etc/apt/sources.list

Ajouter la ligne :

deb http://oss.oracle.com/debian unstable main non-free

Puis faire les commandes :

%> wget http://oss.oracle.com/el4/RPM-GPG-KEY-oracle  -O- | sudo apt-key add -
%> aptitude update
%> aptitude install oracle-x

Le soucis, c’est qu’Oracle XE a des besoins minimum en ressources et qu’il les vérifie avant de s’installer. Sur un mc classique, on va tomber sur un message de ce genre :

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 0 MB
of swap space.  Configure more swap space on the system and retry the installation.

En effet, les mc viennent avec aucun swap… Comme il n’est pas possible de faire une installation d’Oracle XE sans swap, on va essayer d’en créer un. Il veut un swap de 1024 Mo, alors on va lui en donner un en prenant sur l’espace disque :

%> dd if=/dev/zero of=/swapfile bs=1M count=1024
%> mkswap /swapfile
%> swapon /swapfile

Nous pouvons re-essayer d’installer XE :

%> aptitude install oracle-xe

Et configurer XE :

%> /etc/init.d/oracle-xe configure

Attention à ne pas laisser APEX sur le port 8080, nous avons en effet Tomcat qui l’utilise. Prenons le 9090 par exemple.

Nous allons aussi paramétrer bash pour avoir les variables d’environnement pour lancer les commande imp et exp et sqlplus.

On ouvre le fichier .bashrc :

%> vi $HOME/.bashrc

Et on ajoute :

export PATH=$PATH:/usr/lib/oracle/xe/app/oracle/product/10.2.0/server/bin

export ORACLE_HOME=/usr/lib/oracle/xe/app/oracle/product/10.2.0/server

export ORACLE_SID=XE

Charger ces modifications :

%> source $HOME/.bashrc

Nous aimerions bien pouvoir accéder à APEX depuis autre chose que localhost, alors on fait :

%> sqlplus

Se connecter avec l’utilisateur SYSTEM (le mot de passe a été définie à l’étape de configuration d’XE). Et saisir :

SQL> EXEC DBMS_XDB.SETLISTENERLOCALACCESS(FALSE);

L’accès est donc maintenant possible par : http://ip.du.mC:9090/apex/

Utiliser le pilote JDBC d’Oracle

Pour pouvoir se connecter à Oracle depuis Tomcat, nous allons avoir besoin d’ajouter le pilote JDBC aux  bibliothèques chargées par Tomcat :

%> ln -s ${ORACLE_HOME}/jdbc/lib/ojdbc14.jar /usr/share/tomcat6/lib/

Installer MySQL

%> aptitude install libmysql-java

CREATE DATABASE nomDB DEFAULT CHARACTER SET utf8;

GRANT ALL PRIVILEGES ON db_base.* TO db_user @’%’ IDENTIFIED BY ‘db_passwd’;

Redémarrage après archivage du mC

Suite à un arrêt prolongé du mC, il peut changer de nom et d’ip (voir dans le manager ovh). Dans ce cas, Oracle ne sera plus accessible car il est assez sensible à ce genre de paramètres. Il va donc falloir réaliser plusieurs étapes :

Étape 1 : Renommer le host

Le nom publique de la machine est basé sur l’ip, par exemple mc-178-32-111-111.ovh.net. Si l’ip a changé, son nom aussi, cependant la configuration de la VM n’en tient pas compte. Il faut donc éditer le fichier configuration correspondant :

%> vi /etc/hostname

Et adapter le nom du hostname avec le nom affiché dans le manager OVH. Pour que la modification soit prise en compte :

%> service hostname start

On peut vérifier avec :

%> hostname --fqd

Étape 2

Il faut ensuite stopper Oracle XE – si il est actif :

%> service oracle-xe stop

Puis changer sa configuration :

%> vi $ORACLE_HOME/network/admin/listener.ora

Rechercher un bloc qui ressemble à ceci :

LISTENER =
(DESCRIPTION_LIST =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC_FOR_XE))
(ADDRESS = (PROTOCOL = TCP)(HOST = mc-178-32-xxx-xxx.ovh.net)(PORT = 1521))
))

Et adapter le HOST=mc-178… avec la valeur qui a été utilisée dans l’étape 1.

Ensuite on redémarre Oracle :

%> service oracle-xe start

Ne pas s’embêter avec le changement d’IP

Il est aussi possible d’avoir une adresse fixe pour chaque mC. Pour cela, depuis l’interface d’administration du mC, il faut ouvrir le détail du projet courant en cliquant sur l’icône “Éditer ce projet“. Dans le dialogue qui s’ouvre, repérer le champ “Zone (DNS)“. Il faut récupérer cette valeur et la concaténer avec le nom du mC. Exemple :

  • Nom du mc : mon-test
  • Zone DNS : abcde.project.ovh.net
  • Résultat : mon-test.abcde.project.ovh.net
  • Je peux donc accéder au Tomcat installé sur le mC via : http://mon-test.abcde.project.ovh.net:8080

Ce n’est pas très intuitif à mémoriser comme url, mais il est possible de faire une entrée CNAME dans votre DNS. Et hop ! Le tour est joué

_statusDB = null;

Java et les fuites mémoires

Petite série d’article sur les fuites mémoire (memory leak) en Java :

Les outils :

  • MAT qui se base sur Eclipse, mais dispose d’un client indépendant pour ceux qui n’utilisent pas Eclipse
  • VisualVM qui se base sur Netbeans, mais dispose d’un client indépendant pour ceux qui n’utilisent pas Netbeans

Ne pas croire que parce-qu’il y a un garbage collector la gestion de la mémoire doit être ignorée. Surtout dans un contexte d’applications web où l’empilement des class-loaders rend difficile d’avoir une vision claire.