Fedora-Fr - Communauté francophone Fedora - Linux

Planet de Fedora-Fr : la communauté francophone autour de la distribution Linux Fedora

A propos

Cette page est actualisée toutes les heures.

Cette page est une sélection de blogs autour de Fedora. Fedora-Fr.org décline toute responsabilité au sujet des propos tenus par les auteurs des blogs de ce planet. Leurs propos sont leur entière responsabilité.

Le contenu de ce planet appartient à leurs auteurs respectifs. Merci de consulter leur blogs pour obtenir les licences respectives.

Mot-clefs : javascript

Scripts JS avec Java et Rhino : mise en œuvre avec un bot IRC

Fabien Nicoleau

Je montrais dans mon billet précédent que grâce à Rhino il est possible d'exécuter du code JavaScript depuis un programme Java, et comment il possible de se servir de ce système pour gérer des plugins. Je propose dans ce billet de mettre en œuvre ces fonctionnalités et de les appliquer à un projet simple : un robot IRC modulaire. Le principe est d'avoir un robot écrit en Java et capable de se connecter à un serveur IRC, puis de communiquer avec lui (envoi et réception de messages). Nous n'ajouterons rien à ce programme Java, aucun savoir faire pour ce robot, à une exception prêt : la possibilité de stocker des plugins qui lui permettront d'avoir de nouvelles fonctionnalités. Attention, le sujet de ce billet n'étant pas le codage d'un robot IRC (il y en a de meilleurs pour ca ^^), celui présenté ici est ultra simple, et se contente du minimum.

Pour comprendre ce programme, il faut avoir quelques notions sur Rhino/Java, des notions sur les expressions régulières, et notamment les groups qui seront bien utiles ici, et savoir en gros ce qu'est un client IRC.

Le décor

Le programme sera donc codé en Java. Une classe JIrcRobot se chargera de se connecter au serveur IRC, et de communiquer avec lui. Une interface permettra de définir les méthodes à implémenter par les plugins. Elle sera originalement nommée Plugin. La classe JIrcRobot chargera les plugins disponibles, c'est à dire ceux contenus dans le dossier originalement nommé plugins et dont l'extension est .js, puisqu'ils contiennent du code JavaScript. Cela permettra aussi de pouvoir désactiver le chargement d'un plugin en modifiant simplement son extension. Une fois les plugins chargés, le programme attendra des messages du serveur. A chaque message reçu, il interrogera tout ses plugins pour savoir si l'un d'eux est concerné par le message. Si c'est le cas, le plugin sera alors exécuté et pourra alors réagir en envoyant des messages au serveur IRC.

Le test

Pour ceux qui préfèrent voir les choses fonctionner tout de suite, et voir comment ca marche, je vous propose de tester tout de suite le programme. Il vous faut récupérer tous les fichiers en annexe de ce programme : le fichier JIrcRobot.java ainsi que tous les fichiers en .js, qui seront à mettre dans un dossier nommé plugins. JIrcRobot.java sera à mettre au même niveau d'arborescence que le dossier plugins. Vous pouvez sinon juste prendre le fichier jircrobot.tar.gz et le décompresser.

Ouvrez ensuite le fichier JIrcRobot.java et éditez les paramètres de connexion, situés sous le commentaire "CONFIGURATION DE LA CONNEXION AU SERVEUR" pour donner un pseudonyme au robot, lui indiquer sur quel serveur il doit se connecter, et quels canaux il doit rejoindre. Sauvegardez les modifications et compilez le programme :

javac JIrcRobot.java

Vous pouvez maintenant l'exécuter :

java JIrcRobot

Le robot charge les plugins puis se connecte au serveur. En l'état actuel, le robot est capable de répondre aux PING du serveur, afin qu'il ne coupe pas la connexion, de rejoindre des canaux de discussion, de se présenter quand il rejoint un canal et souhaiter la bienvenue à ceux qui arrivent ensuite, de se couper si quelqu'un tape "!QUIT" et enfin de crier "COCORICO" si une phrase contient "france". Ces fonctionnalités sont toutes apportées par les plugins. Le programme Java en lui même ne sait rien faire de tout ca.

La classe JIrcRobot

Il est maintenant temps de comprendre comment tout cela fonctionne. Je passe rapidement sur les fonctionnalités liées au protocole IRC. Sans entrer dans le code, il faut juste savoir que la classe propose ces méthodes :

connect() : se connecte au serveur IRC
getNickName() : renvoie le pseudonyme du robot
joinChannels() : envoie les commandes au serveur IRC pour rejoindre des canaux de discussion
loadPlugins() : charge tous les plugins disponibles dans un dossier
readLine() : lis un message provenant du serveur, s'il y en a un d'arrivé
run() : boucle conservant la connexion avec le serveur, et exécutant les plugins si besoin
setStayConnected() : permet d'indiquer si l'on souhaite rester connecté au serveur ou non
write() : envoi un message au serveur IRC


Les méthodes importantes seront détaillées ensuite : loadPlugins et run.

Enfin la classe contient une interface privée, Plugin, qui permet de définir les méthodes que devront implémenter les plugins :

getPattern() : retournera l'expression régulière qui si elle est validée, indiquera que le plugin doit réagir au message arrivé
patternMatch() : cette méthode sera appelée si l'expression régulière du plugin match avec le message arrivé

Nos plugins devront donc implémenter ces deux méthodes.

Le programme Java

Après avoir vu les méthodes permettant de dialoguer avec le serveur, intéressons nous, dans le détail cette fois-ci, au code des deux principales. Avant cela, un rapide coup d'œil à la méthode main du programme :

public static void main(String[] args) {
    JIrcRobot jir = new JIrcRobot();
    jir.loadPlugins("plugins");
    if (jir.connect()) {
        jir.run();
    }
}

Rien de complexe : on créé un objet JIrcRobot, on charge les plugins, on connecte le robot au serveur IRC, et enfin on se met en attente de réception des messages avec la méthode run(). Il est maintenant temps de voir comment sont chargés les plugins, avec la méthode loadPlugins ;

public void loadPlugins(String folder) {
    ScriptEngine engine;
    Invocable inv;
    File dir = new File(folder);
    for (File file : dir.listFiles()) {
        if (file.isFile() && file.getName().toLowerCase().endsWith(".js")) {
            try {
                engine = new ScriptEngineManager().getEngineByName("JavaScript");
                engine.put("robot", this);
                engine.eval(new FileReader(file.getAbsolutePath()));
                inv = (Invocable) engine;
                this.plugins.add(inv.getInterface(Plugin.class));
                System.out.println(file.getAbsolutePath() + " loaded.");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

Cette méthode va lister (listFiles) le dossier dont le nom lui est passé en paramètre (folder). Pour chaque fichier dont l'extension est .js (endsWith), elle créé un moteur de JavaScript (getEngineByName) et place dans l'environnement du script une variable nommée robot pointant sur l'objet JIrcRobot (put). Ensuite elle lit le contenu du fichier et stocke dans le tableau plugins une instanciation de l'interface Plugin provenant du fichier (getInterface).

Le chargement des plugins consiste donc à stocker des objets implémentant l'interface Plugin et dont le contenu provient des fichiers JavaScript.

Maintenant, la méthode run :

public void run() {
    String buffer;
    Matcher matcher;
    while (this.stayConnected) {
        buffer = this.readLine();
        if (!buffer.equals("")) {
            System.out.println(buffer);
            for (Plugin p : this.plugins) {
                matcher = Pattern.compile(p.getPattern()).matcher(buffer);
                if (matcher.find()) {
                    p.patternMatch(matcher);
                }
            }
        }
    }
}

Cette méthode contient une boucle qui attend un message du serveur IRC (readLine). Une fois qu'un message est reçu, elle boucle sur tous les plugins chargés. Pour chacun d'eux, elle teste si le pattern du plugin (p.getPattern()) match avec le message reçu (buffer). Si c'est le cas (find), la méthode patterMatch() du plugin est appellée et on lui passe en paramètre le matcher, ce qui permettra au plugin d'analyser les groupes capturés grâce à l'expression régulière. C'est assez pratique car le plugin reçoit ainsi seulement ce qui l'intéresse, et qu'il a lui-même spécifié dans la regex.

C'est ici que s'arrête le détail ! Nous avons un système qui stocke des plugins et un autre qui communique avec eux pour savoir s'il faut les solliciter (grâce à un système de regex ici, mais ca pourrait être autrement) et qui qui les exécute si besoin. Ce robot IRC est donc ainsi fait d'un noyau, disposant de peu de fonctionnalités, mais extensible à volonté.

Les plugins

Notre programme étant prêt, concentrons nous maintenant sur les plugins. Nous créerons un fichier par plugin

ping.js

Le serveur IRC envoie régulièrement un message PING auquel le client doit répondre. Si la réponse n'est pas envoyée dans un certain laps de temps, le serveur rompt la connexion. Ce plugin est donc vital. Le message est du type "PING :msg". Le client doit répondre "PONG :msg". msg est parfois le nom du serveur, ou parfois un numéro aléatoire. Pour un PING reçu, on récupèrera donc la partie droite, et on s'en servira pour envoyer le PONG. Voici le contenu de ping.js :

importClass(java.util.regex.Matcher);

function getPattern() {
    return 'PING\\s:(.*)';
}

function patternMatch(matcher) {
    robot.write("PONG :" + matcher.group(1));
}

On commence par importer la classe Java Matcher. On implémente ensuite les deux méthodes nécessaires de l'interface Plugin. Le regex renvoyé par getPattern contient un groupe qui capturera la partie à renvoyer par le PONG. La fonction patternMatch qui est appelée si le regex a matché envoie le PONG suivi du groupe capturé dans le PING. Pour cela elle utilise la variable robot représentant l'objet JIrcRobot pour envoyer un message au serveur.

endOfMOTD.js

Le Message Of The Day (MOTD) est un texte envoyé par le serveur au client lorsqu'il se connecte. Il contient souvent des informations sur le serveur. Une fois le MOTD envoyé, le serveur envoie un message indiquant la fin du MOTD du type : ":calvino.freenode.net 376 jircrobot :End of /MOTD command.". Il est important de réagir à ce message car certains serveurs n'autorisent pas le client à rejoindre des canaux tant que le MOTD n'a pas été envoyé. En attendant la réception de ce message pour rejoindre des canaux, on est sûr qu'on ne sera pas bloqué. Ce plugin réagira donc à ce message, et une fois reçu, appellera la méthode joinChannels de la classe JIrcRobot :

importClass(java.util.regex.Matcher);

function getPattern() {
    return ':.*\\s376\\s.*';
}

function patternMatch(matcher) {
    robot.joinChannels();
}

C'est assez simple ici encore. On donne la regex pour capturer la fin du MOTD et lorsque que le message est arrivé, on utilise la variable robot pour se connecter aux canaux.

quit.js

Ce plugin permettra de stopper le bot. Lorsqu'une personne tapera !QUIT sur un canal, le robot se stoppera. Biensûr dans le cadre d'un "vrai" bot, il faudrait s'assurer que la personne voulant couper le robot en a le droit, en se basant sur son nom d'hôte ou son pseudonyme. Le message envoyé par le serveur sera du type ":someone!~someident@some-host.com PRIVMSG #channel1 :!QUIT". Voici le contenu du plugin :

importClass(java.util.regex.Matcher);

function getPattern() {
    return ':.*\\sPRIVMSG\\s[^ ]+\\s:!QUIT';
}

function patternMatch(matcher) {
    robot.setStayConnected(false);
}

La regex ne capture rien ici car on ne gère pas de droits. Pour quelque chose de plus sécurisé, il faudrait capturer au moins le pseudonyme de la personne. La fonction appelée utilisera la méthode setStayConnected avec false en paramètre, ce qui aura pour effet de stopper la méthode run du robot.

join.js

Afin de rendre notre robot un minimum sociable, ce plugin aura pour tâche de le faire se présenter lorsqu'il rejoint un canal et de souhaiter la bienvenue aux nouveaux arrivants. Pour cela, il suffira de capturer les messages de type JOIN provenant du serveur, qui sont envoyés quand une personne rejoint un canal, ou quand le bot lui même le fait. Le message est du type ":nick!~ident@hostname.fr JOIN :#channel". Il suffit alors de comparer "nick" avec le propre pseudonyme du robot. S'il sont équivalents, c'est que c'est lui qui entre sur un canal, il doit donc se présenter. Si ce n'est pas le sien, alors il souhaite la bienvenue au nouvel arrivant. Il faudra donc capturer le pseudonyme et le nom du canal afin de s'en servir pour envoyer le message :

importClass(java.util.regex.Matcher);

function getPattern() {
    return ':([^!]+).*\\sJOIN\\s:([^ ]+)';
}

function patternMatch(matcher) {
    // Si c'est le robot qui JOIN
    if(matcher.group(1) == robot.getNickname()) {
        robot.write("PRIVMSG "+matcher.group(2)+" :Salut tout le monde, je suis "+matcher.group(1));
    }
    else { // si c'est un nouvel arrivant
        robot.write("PRIVMSG "+matcher.group(2)+" :Salut "+matcher.group(1));
    }
}

On voit donc la regex avec les deux captures (nick + canal). Ces informations sont ensuite utilisées pour le message envoyé.

patriote.js

Un plugin beaucoup moins utile, qui permettra simplement de montrer comment réagir à un texte particulier. Nous souhaitons ici que le bot montre qu'il est fier d'être français. A chaque fois que le mot France (peu importe la casse) sera écrit, il réagira. Le message reçu sera du type ":someone!~someident@some-host.com PRIVMSG #channel :Vive la France". Voyons le code du plugin :

importClass(java.util.regex.Matcher);

function getPattern() {
    return '(?i):[^!]+.*\\sPRIVMSG\\s([^ ]+)\\s:.*france.*';
}

function patternMatch(matcher) {
    robot.write("PRIVMSG "+matcher.group(1)+" :COCORICO !!!");
}

La regex indique que la recherche sera insensible à la casse. On capture simplement le nom du canal d'où le texte est envoyé pour pouvoir répondre.


Voilà pour quelques exemples de plugins. Le but ici est de montrer que presque tout est possible, mais que surtout ce genre de programme, modulable, auquel on peut ajouter des fonctionnalités, est facilement réalisable. Pour les plus courageux qui auront lu jusque ici (j'espère que number80 en fait partie !), n'hésitez pas à poser des questions dans les commentaires. Il n'est pas facile de synthétiser tout cela en un seul billet, il y a donc probablement des passages obscurs !


Fabien (eponyme)

Scripts JS avec Java et Rhino : mise en œuvre avec un bot IRC

Fabien Nicoleau

Je montrais dans mon billet précédent que grâce à Rhino il est possible d'exécuter du code JavaScript depuis un programme Java, et comment il possible de se servir de ce système pour gérer des plugins. Je propose dans ce billet de mettre en œuvre ces fonctionnalités et de les appliquer à un projet simple : un robot IRC modulaire. Le principe est d'avoir un robot écrit en Java et capable de se connecter à un serveur IRC, puis de communiquer avec lui (envoi et réception de messages). Nous n'ajouterons rien à ce programme Java, aucun savoir faire pour ce robot, à une exception prêt : la possibilité de stocker des plugins qui lui permettront d'avoir de nouvelles fonctionnalités. Attention, le sujet de ce billet n'étant pas le codage d'un robot IRC (il y en a de meilleurs pour ca ^^), celui présenté ici est ultra simple, et se contente du minimum.

Pour comprendre ce programme, il faut avoir quelques notions sur Rhino/Java, des notions sur les expressions régulières, et notamment les groups qui seront bien utiles ici, et savoir en gros ce qu'est un client IRC.

Le décor

Le programme sera donc codé en Java. Une classe JIrcRobot se chargera de se connecter au serveur IRC, et de communiquer avec lui. Une interface permettra de définir les méthodes à implémenter par les plugins. Elle sera originalement nommée Plugin. La classe JIrcRobot chargera les plugins disponibles, c'est à dire ceux contenus dans le dossier originalement nommé plugins et dont l'extension est .js, puisqu'ils contiennent du code JavaScript. Cela permettra aussi de pouvoir désactiver le chargement d'un plugin en modifiant simplement son extension. Une fois les plugins chargés, le programme attendra des messages du serveur. A chaque message reçu, il interrogera tout ses plugins pour savoir si l'un d'eux est concerné par le message. Si c'est le cas, le plugin sera alors exécuté et pourra alors réagir en envoyant des messages au serveur IRC.

Le test

Pour ceux qui préfèrent voir les choses fonctionner tout de suite, et voir comment ca marche, je vous propose de tester tout de suite le programme. Il vous faut récupérer tous les fichiers en annexe de ce programme : le fichier JIrcRobot.java ainsi que tous les fichiers en .js, qui seront à mettre dans un dossier nommé plugins. JIrcRobot.java sera à mettre au même niveau d'arborescence que le dossier plugins. Vous pouvez sinon juste prendre le fichier jircrobot.tar.gz et le décompresser.

Ouvrez ensuite le fichier JIrcRobot.java et éditez les paramètres de connexion, situés sous le commentaire "CONFIGURATION DE LA CONNEXION AU SERVEUR" pour donner un pseudonyme au robot, lui indiquer sur quel serveur il doit se connecter, et quels canaux il doit rejoindre. Sauvegardez les modifications et compilez le programme :

javac JIrcRobot.java

Vous pouvez maintenant l'exécuter :

java JIrcRobot

Le robot charge les plugins puis se connecte au serveur. En l'état actuel, le robot est capable de répondre aux PING du serveur, afin qu'il ne coupe pas la connexion, de rejoindre des canaux de discussion, de se présenter quand il rejoint un canal et souhaiter la bienvenue à ceux qui arrivent ensuite, de se couper si quelqu'un tape "!QUIT" et enfin de crier "COCORICO" si une phrase contient "france". Ces fonctionnalités sont toutes apportées par les plugins. Le programme Java en lui même ne sait rien faire de tout ca.

La classe JIrcRobot

Il est maintenant temps de comprendre comment tout cela fonctionne. Je passe rapidement sur les fonctionnalités liées au protocole IRC. Sans entrer dans le code, il faut juste savoir que la classe propose ces méthodes :

connect() : se connecte au serveur IRC
getNickName() : renvoie le pseudonyme du robot
joinChannels() : envoie les commandes au serveur IRC pour rejoindre des canaux de discussion
loadPlugins() : charge tous les plugins disponibles dans un dossier
readLine() : lis un message provenant du serveur, s'il y en a un d'arrivé
run() : boucle conservant la connexion avec le serveur, et exécutant les plugins si besoin
setStayConnected() : permet d'indiquer si l'on souhaite rester connecté au serveur ou non
write() : envoi un message au serveur IRC


Les méthodes importantes seront détaillées ensuite : loadPlugins et run.

Enfin la classe contient une interface privée, Plugin, qui permet de définir les méthodes que devront implémenter les plugins :

getPattern() : retournera l'expression régulière qui si elle est validée, indiquera que le plugin doit réagir au message arrivé
patternMatch() : cette méthode sera appelée si l'expression régulière du plugin match avec le message arrivé

Nos plugins devront donc implémenter ces deux méthodes.

Le programme Java

Après avoir vu les méthodes permettant de dialoguer avec le serveur, intéressons nous, dans le détail cette fois-ci, au code des deux principales. Avant cela, un rapide coup d'œil à la méthode main du programme :

public static void main(String[] args) {
    JIrcRobot jir = new JIrcRobot();
    jir.loadPlugins("plugins");
    if (jir.connect()) {
        jir.run();
    }
}

Rien de complexe : on créé un objet JIrcRobot, on charge les plugins, on connecte le robot au serveur IRC, et enfin on se met en attente de réception des messages avec la méthode run(). Il est maintenant temps de voir comment sont chargés les plugins, avec la méthode loadPlugins ;

public void loadPlugins(String folder) {
    ScriptEngine engine;
    Invocable inv;
    File dir = new File(folder);
    for (File file : dir.listFiles()) {
        if (file.isFile() && file.getName().toLowerCase().endsWith(".js")) {
            try {
                engine = new ScriptEngineManager().getEngineByName("JavaScript");
                engine.put("robot", this);
                engine.eval(new FileReader(file.getAbsolutePath()));
                inv = (Invocable) engine;
                this.plugins.add(inv.getInterface(Plugin.class));
                System.out.println(file.getAbsolutePath() + " loaded.");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

Cette méthode va lister (listFiles) le dossier dont le nom lui est passé en paramètre (folder). Pour chaque fichier dont l'extension est .js (endsWith), elle créé un moteur de JavaScript (getEngineByName) et place dans l'environnement du script une variable nommée robot pointant sur l'objet JIrcRobot (put). Ensuite elle lit le contenu du fichier et stocke dans le tableau plugins une instanciation de l'interface Plugin provenant du fichier (getInterface).

Le chargement des plugins consiste donc à stocker des objets implémentant l'interface Plugin et dont le contenu provient des fichiers JavaScript.

Maintenant, la méthode run :

public void run() {
    String buffer;
    Matcher matcher;
    while (this.stayConnected) {
        buffer = this.readLine();
        if (!buffer.equals("")) {
            System.out.println(buffer);
            for (Plugin p : this.plugins) {
                matcher = Pattern.compile(p.getPattern()).matcher(buffer);
                if (matcher.find()) {
                    p.patternMatch(matcher);
                }
            }
        }
    }
}

Cette méthode contient une boucle qui attend un message du serveur IRC (readLine). Une fois qu'un message est reçu, elle boucle sur tous les plugins chargés. Pour chacun d'eux, elle teste si le pattern du plugin (p.getPattern()) match avec le message reçu (buffer). Si c'est le cas (find), la méthode patterMatch() du plugin est appellée et on lui passe en paramètre le matcher, ce qui permettra au plugin d'analyser les groupes capturés grâce à l'expression régulière. C'est assez pratique car le plugin reçoit ainsi seulement ce qui l'intéresse, et qu'il a lui-même spécifié dans la regex.

C'est ici que s'arrête le détail ! Nous avons un système qui stocke des plugins et un autre qui communique avec eux pour savoir s'il faut les solliciter (grâce à un système de regex ici, mais ca pourrait être autrement) et qui qui les exécute si besoin. Ce robot IRC est donc ainsi fait d'un noyau, disposant de peu de fonctionnalités, mais extensible à volonté.

Les plugins

Notre programme étant prêt, concentrons nous maintenant sur les plugins. Nous créerons un fichier par plugin

ping.js

Le serveur IRC envoie régulièrement un message PING auquel le client doit répondre. Si la réponse n'est pas envoyée dans un certain laps de temps, le serveur rompt la connexion. Ce plugin est donc vital. Le message est du type "PING :msg". Le client doit répondre "PONG :msg". msg est parfois le nom du serveur, ou parfois un numéro aléatoire. Pour un PING reçu, on récupèrera donc la partie droite, et on s'en servira pour envoyer le PONG. Voici le contenu de ping.js :

importClass(java.util.regex.Matcher);

function getPattern() {
    return 'PING\\s:(.*)';
}

function patternMatch(matcher) {
    robot.write("PONG :" + matcher.group(1));
}

On commence par importer la classe Java Matcher. On implémente ensuite les deux méthodes nécessaires de l'interface Plugin. Le regex renvoyé par getPattern contient un groupe qui capturera la partie à renvoyer par le PONG. La fonction patternMatch qui est appelée si le regex a matché envoie le PONG suivi du groupe capturé dans le PING. Pour cela elle utilise la variable robot représentant l'objet JIrcRobot pour envoyer un message au serveur.

endOfMOTD.js

Le Message Of The Day (MOTD) est un texte envoyé par le serveur au client lorsqu'il se connecte. Il contient souvent des informations sur le serveur. Une fois le MOTD envoyé, le serveur envoie un message indiquant la fin du MOTD du type : ":calvino.freenode.net 376 jircrobot :End of /MOTD command.". Il est important de réagir à ce message car certains serveurs n'autorisent pas le client à rejoindre des canaux tant que le MOTD n'a pas été envoyé. En attendant la réception de ce message pour rejoindre des canaux, on est sûr qu'on ne sera pas bloqué. Ce plugin réagira donc à ce message, et une fois reçu, appellera la méthode joinChannels de la classe JIrcRobot :

importClass(java.util.regex.Matcher);

function getPattern() {
    return ':.*\\s376\\s.*';
}

function patternMatch(matcher) {
    robot.joinChannels();
}

C'est assez simple ici encore. On donne la regex pour capturer la fin du MOTD et lorsque que le message est arrivé, on utilise la variable robot pour se connecter aux canaux.

quit.js

Ce plugin permettra de stopper le bot. Lorsqu'une personne tapera !QUIT sur un canal, le robot se stoppera. Biensûr dans le cadre d'un "vrai" bot, il faudrait s'assurer que la personne voulant couper le robot en a le droit, en se basant sur son nom d'hôte ou son pseudonyme. Le message envoyé par le serveur sera du type ":someone!~someident@some-host.com PRIVMSG #channel1 :!QUIT". Voici le contenu du plugin :

importClass(java.util.regex.Matcher);

function getPattern() {
    return ':.*\\sPRIVMSG\\s[^ ]+\\s:!QUIT';
}

function patternMatch(matcher) {
    robot.setStayConnected(false);
}

La regex ne capture rien ici car on ne gère pas de droits. Pour quelque chose de plus sécurisé, il faudrait capturer au moins le pseudonyme de la personne. La fonction appelée utilisera la méthode setStayConnected avec false en paramètre, ce qui aura pour effet de stopper la méthode run du robot.

join.js

Afin de rendre notre robot un minimum sociable, ce plugin aura pour tâche de le faire se présenter lorsqu'il rejoint un canal et de souhaiter la bienvenue aux nouveaux arrivants. Pour cela, il suffira de capturer les messages de type JOIN provenant du serveur, qui sont envoyés quand une personne rejoint un canal, ou quand le bot lui même le fait. Le message est du type ":nick!~ident@hostname.fr JOIN :#channel". Il suffit alors de comparer "nick" avec le propre pseudonyme du robot. S'il sont équivalents, c'est que c'est lui qui entre sur un canal, il doit donc se présenter. Si ce n'est pas le sien, alors il souhaite la bienvenue au nouvel arrivant. Il faudra donc capturer le pseudonyme et le nom du canal afin de s'en servir pour envoyer le message :

importClass(java.util.regex.Matcher);

function getPattern() {
    return ':([^!]+).*\\sJOIN\\s:([^ ]+)';
}

function patternMatch(matcher) {
    // Si c'est le robot qui JOIN
    if(matcher.group(1) == robot.getNickname()) {
        robot.write("PRIVMSG "+matcher.group(2)+" :Salut tout le monde, je suis "+matcher.group(1));
    }
    else { // si c'est un nouvel arrivant
        robot.write("PRIVMSG "+matcher.group(2)+" :Salut "+matcher.group(1));
    }
}

On voit donc la regex avec les deux captures (nick + canal). Ces informations sont ensuite utilisées pour le message envoyé.

patriote.js

Un plugin beaucoup moins utile, qui permettra simplement de montrer comment réagir à un texte particulier. Nous souhaitons ici que le bot montre qu'il est fier d'être français. A chaque fois que le mot France (peu importe la casse) sera écrit, il réagira. Le message reçu sera du type ":someone!~someident@some-host.com PRIVMSG #channel :Vive la France". Voyons le code du plugin :

importClass(java.util.regex.Matcher);

function getPattern() {
    return '(?i):[^!]+.*\\sPRIVMSG\\s([^ ]+)\\s:.*france.*';
}

function patternMatch(matcher) {
    robot.write("PRIVMSG "+matcher.group(1)+" :COCORICO !!!");
}

La regex indique que la recherche sera insensible à la casse. On capture simplement le nom du canal d'où le texte est envoyé pour pouvoir répondre.


Voilà pour quelques exemples de plugins. Le but ici est de montrer que presque tout est possible, mais que surtout ce genre de programme, modulable, auquel on peut ajouter des fonctionnalités, est facilement réalisable. Pour les plus courageux qui auront lu jusque ici (j'espère que number80 en fait partie !), n'hésitez pas à poser des questions dans les commentaires. Il n'est pas facile de synthétiser tout cela en un seul billet, il y a donc probablement des passages obscurs !


Fabien (eponyme)

Java : gérer facilement des plugins JavaScript

Fabien Nicoleau

Dans une application, il peut être parfois intéressant d'extraire une partie de l'intelligence afin de répondre à un besoins sépcifique. Un exemple courant est une application fournie à différents clients. Elle comporte un socle commun, puis les spécificités sont développées dans des plugins afin de ne pas avoir à toucher au code de l'application pour une demande particulière. L'autre intérêt est de pouvoir charger dans une application des fonctionnalités supplémentaires, permettant de réduire le poids de l'application en retirant des fonctionnalités inutilisées. Le fait d'avoir du code externe permet aussi d'en facilité les mises à jour.

En Java, il est possible de créer des plugins de différentes façons : avec des classes externes, chargées grâce à un ClassLoader, grâce à groovy, très populaire, ou encore jython, permettant d'exécuter du code Python depuis un programme Java. Il existe de nombreuses autres solutions, comme celle d'utiliser le moteur JavaScript de la fondation Mozilla : Rhino. Sa particularité est qu'il est fourni par défaut avec Java depuis la version 6.

En ayant eu besoin pour un projet, et ayant trouvé peu d'aide en français, je propose ici quelques exemples très basiques afin de mettre en place un système de plugins JavaScript avec Java. 90% de l'aide dont j'ai eu besoin a été trouvée sur cette page de documentation. Je reprends donc ici quelques exemples, commentés, afin de comprendre le principe et d'imaginer les possibilités qu'offre Rhino. N'hésitez pas à poser des questions dans les commentaires si vous avez besoins de précisions.

Tout d'abord, je vous indique ici les import dont vous aurez besoin et qui seront à utiliser tout au long des exemples (mais pas utiles pour tous). Il ne seront pas réécris ensuite, pour plus de lisibilité :

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.io.FileReader;

Toujours pour simplifier le code affiché, les exceptions ne seront pas gérées, mais renvoyées par le main. Il vous faut évidemment avoir quelques bases de Java et de JavaScript pour pouvoir suivre ces exemples. Dans chacun d'eux, j'utiliserai des fonctions JavaScript. Si vous préférez utiliser du code orienté objet, il vous sera facile de l'adapter grâce aux exemples de la page Oracle citée en préambule.

Enfin, pour chaque exemple, je n'afficherai que la fonction main, qu'il faudra évidemment que vous incluiez dans une classe pour que le tout fonctionne, cependant les sources de chaque exemples sont disponibles en annexe de ce billet.

Exemple 1 : un simple affichage

Pour le premier essai, nous allons exécuter une simple ligne de code JavaScript dans du code Java :

public static void main(String[] args) throws Exception {
     ScriptEngineManager manager = new ScriptEngineManager();
     ScriptEngine engine = manager.getEngineByName("JavaScript");
     engine.eval("println('Mon premier script !');");
}

Rien de plus ! C'est en fait assez simple. Nous commençons par instancier un ScriptEngineManager qui nous permettra de charger le moteur de script souhaité. Nous l'utilisons sur la deuxième ligne : nous chargeons le moteur JavaScript. Aucune installation de bibliothèque nécessaire, car ce moteur est fourni avec Java 6 ! Ces deux premières lignes seront utilisées dans tous nos exemples. Nous terminons en demandant à notre moteur d'évaluer une ligne de code, ici l'affichage du texte "Mon premier script !".

Après compilation et exécution, la ligne s'affiche bien. Rien de transcendant ici, mais nous voyons que nous allons facilement pouvoir exécuter du code JavaScript.

Exemple 2 : interagir avec un script externe

Dans l'optique d'utiliser un système de plugins, il nous faut d'abord pouvoir sortir le code JavaScript du code Java, et le placer dans un fichier externe. De plus il nous faut pouvoir appeler une fonction en lui passant des paramètres, puis pouvoir récupérer le résultat obtenu.

Pour illustrer l'exemple, nous allons imaginer un programme Java qui passe à une fonction nommée compute() deux chiffres qu'elle traitera. Le code Java récupèrera le résultat du traitement effectué par compute() et l'affichera. Enfin, nous allons créer deux plugins JavaScript qui implémenteront tous les deux la fonction compute(). L'un fera une addition (plus.js) des deux chiffres donnés, l'autre une soustraction (moins.js). On commence par le code Java :

public static void main(String[] args) throws Exception {
     ScriptEngineManager manager = new ScriptEngineManager();
     ScriptEngine engine = manager.getEngineByName("JavaScript");
     engine.eval(new FileReader("plus.js"));
     Invocable inv = (Invocable) engine;
     System.out.println(inv.invokeFunction("compute",3,2));
}

Comme dans le premier exemple, nous récupérons le moteur JavaScript. Ensuite plutôt que directement évaluer du code, nous passons un fichier à la fonction eval(), qui contiendra notre code JavaScript. Nous commençons ici par plus.js, qui fera une addition. Vous pourrez ensuite utiliser moins.js. Enfin, nous invoquons la fonction compute() et lui passons deux arguments (3 et 2). Nous récupérons le résultat du traitement, qui est un Object, et qui contiendra ici un chiffre (un Double à vrai dire), nous l'affichons directement.

Voici le contenu de plus.js :

function compute(a,b) {
   return a+b;
}

Celui de moin.js :

function compute(a,b) {
   return a-b;
}

Aucun commentaire nécessaire ! Exécutez le programme avec plus.js, puis moins.js, les résultats seront 5.0 puis 1.0.

Nous voyons ici que nous pouvons très facilement avoir du code Java qui exécute une fonction dont il ne connait pas le contenu. Il en récupère le résultat sous forme d'un objet et peut le traiter. D'un autre coté, nous avons des fichiers contenant du code JavaScript qui traitent de façon différente deux arguments identiques, et renvoient le résultat. Dans une application gérant des plugins, il suffit alors que le nom du fichier JavaScript soit une variable, ou provienne d'un choix de l'utilisateur, et le tour est joué ! Attention tout de même, pour un système robuste, il est important de gérer les exceptions afin de réagir aux problèmes qui pourraient survenir.

Exemple 3 : Exporter des variables dans le script externe

Lorsque l'on mets en place un système de plugins, on souhaite que ces derniers puissent avoir accès aux informations de l'application, souvent par l'intermédiaire d'un noyau depuis lequel on accède à des variables. De cette manière le plugin agit de façon directe avec l'application, en consultant des informations, et en les mettant à jour. Nous allons dans cet exemple voir qu'il est tout à fait possible de faire cela avec Java et Rhino. Nous allons créer une classe Afficheur contenant la méthode affiche() qui se contente d'afficher dans la console une ligne de texte. Ensuite, nous allons instancier un objet de cette classe et l'exporter dans notre script JavaScript. Ce dernier utilisera dans son code l'objet exporté et appellera la méthode affiche() :

private static class Afficheur {
   public void affiche() {
       System.out.println("J'affiche !");
    }
}
public static void main(String[] args) throws Exception {
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("JavaScript");
    engine.eval(new FileReader("afficheur.js"));
    Afficheur aff = new Afficheur();
    engine.put("monAfficheur",aff);
    Invocable inv = (Invocable) engine;
    inv.invokeFunction("affiche");
}

On voit ici qu'une fois l'objet Afficheur instancié, on l'exporte dans le script grace à la méthode put(). On indique que le nom de variable par laquelle l'objet sera accessible dans le script sera "monAfficheur". Enfin on appelle la fonction affiche() implémentée dans le script afficheur.js dont voici le contenu :

function affiche() {
   monAfficheur.affiche();
}

La fonction affiche() utilise donc la variable monAfficheur disponnible grace à l'export fait depuis le code Java, et appelle la méthode affiche(). Voilà notre liaison réalisée ! Depuis notre code JavaScript, nous accédons à un objet de notre application Java et pouvons l'utiliser. Ici nous ne réalisons qu'un simple affichage, mais tout est possible. Notez qu'il est possible de définir différent scope lors de l'export des variables permettant ainsi de leur donner des valeurs différentes selon le contexte utilisé lors de l'évaluation. Plus de renseignement dans la documentation.

Exemple 4 : Implémenter une interface

Nous progressons dans les fonctionnalités proposées pour réaliser une gestion de plugins. Mais il est possible d'aller plus loin, et de définir plus précisément les fonctionnalités offertes par les plugins. Pour cela, nous pouvons définir en Java une interface, et l'implémenter depuis un plugin. Une fois le code du plugin évalué, nous appelons une méthode depuis le code Java, dont l'implémentation a été réalisée depuis le code JavaScript ! Il devient alors possible de définir proprement en Java les méthodes qui seront à implémenter par les plugins, sans en connaitre le traitement évidemment, et d'utiliser ensuite dans le code des objets instanciant cette interface. Mieux que du blabla, le code : 

private interface Afficheur2 {
    void affiche2();
}
public static void main(String[] args) throws Exception {
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("JavaScript");
    engine.eval("function affiche2() { println('affiche (2)'); }");
    Invocable inv = (Invocable) engine;
    Afficheur2 aff = inv.getInterface(Afficheur2.class);
    aff.affiche2();
}

Pour l'exemple, je n'utilise pas de fichier externe, mais directement une ligne de code JavaScript. Ce code implémente la méthode affiche2() de l'interface Afficheur2 en faisant un simple affichage de ligne. Nous créons un objet de type Afficheur2 puis récupérons l'instanciation de l'interface grâce à la méthode getInterface().

Enfin, nous utilisons ce nouvel objet et appelons la méthode affiche2() qui effectuera ce qui a été défini dans le code JavaScript, c'est à dire afficher une ligne de texte.

Nous voyons ici qu'il nous est donc possible d'écrire du code générique et de l'utiliser. C'est grâce aux plugins utilisés que instanciation est faite et nous pouvons ainsi pour un même code Java obtenir des comportement totalement différents.

Exemple 5 : Utilisation des classes Java depuis le code JavaScript

Comme dernier exemple et pour montrer toute la puissance de Rhino, nous créons un code Java qui se contentera d'évaluer un script :

public static void main(String[] args) throws Exception {
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("JavaScript");
    engine.eval(new FileReader("frame.js"));
}

Le script en revanche est particulier, car il importe directement des classes Java et utilise les objets instanciés. Le script frame.js va créer une JFrame et l'afficher. Il affichera aussi le titre de la fenêtre, qu'il récupère directement depuis la propriété de la JFrame :

importClass(javax.swing.JFrame);
importClass(java.awt.Dimension);

var frame = new JFrame("hello");
frame.setSize(new Dimension(250,250));
frame.setVisible(true);
println(frame.title);

Exécutez le programme, une fenêtre apparait !

JFrame depuis un JavaScript


Voilà pour cette petite initiation au scripting avec Java et son application avec l'utilisation de plugins. Il y aurait surement beaucoup plus à dire, mais vous disposez maintenant d'une base pour débuter, et de l'alliance du Java et du JavaScript pour vos applications ! Ce sujet aura aussi été pour moi l'occasion de reprendre la plume sur ce blog, chose qui n'avait pas été faite depuis trop longtemps !


Fabien (eponyme)

Java : gérer facilement des plugins JavaScript

Fabien Nicoleau

Dans une application, il peut être parfois intéressant d'extraire une partie de l'intelligence afin de répondre à un besoins sépcifique. Un exemple courant est une application fournie à différents clients. Elle comporte un socle commun, puis les spécificités sont développées dans des plugins afin de ne pas avoir à toucher au code de l'application pour une demande particulière. L'autre intérêt est de pouvoir charger dans une application des fonctionnalités supplémentaires, permettant de réduire le poids de l'application en retirant des fonctionnalités inutilisées. Le fait d'avoir du code externe permet aussi d'en facilité les mises à jour.

En Java, il est possible de créer des plugins de différentes façons : avec des classes externes, chargées grâce à un ClassLoader, grâce à groovy, très populaire, ou encore jython, permettant d'exécuter du code Python depuis un programme Java. Il existe de nombreuses autres solutions, comme celle d'utiliser le moteur JavaScript de la fondation Mozilla : Rhino. Sa particularité est qu'il est fourni par défaut avec Java depuis la version 6.

En ayant eu besoin pour un projet, et ayant trouvé peu d'aide en français, je propose ici quelques exemples très basiques afin de mettre en place un système de plugins JavaScript avec Java. 90% de l'aide dont j'ai eu besoin a été trouvée sur cette page de documentation. Je reprends donc ici quelques exemples, commentés, afin de comprendre le principe et d'imaginer les possibilités qu'offre Rhino. N'hésitez pas à poser des questions dans les commentaires si vous avez besoins de précisions.

Tout d'abord, je vous indique ici les import dont vous aurez besoin et qui seront à utiliser tout au long des exemples (mais pas utiles pour tous). Il ne seront pas réécris ensuite, pour plus de lisibilité :

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.io.FileReader;

Toujours pour simplifier le code affiché, les exceptions ne seront pas gérées, mais renvoyées par le main. Il vous faut évidemment avoir quelques bases de Java et de JavaScript pour pouvoir suivre ces exemples. Dans chacun d'eux, j'utiliserai des fonctions JavaScript. Si vous préférez utiliser du code orienté objet, il vous sera facile de l'adapter grâce aux exemples de la page Oracle citée en préambule.

Enfin, pour chaque exemple, je n'afficherai que la fonction main, qu'il faudra évidemment que vous incluiez dans une classe pour que le tout fonctionne, cependant les sources de chaque exemples sont disponibles en annexe de ce billet.

Exemple 1 : un simple affichage

Pour le premier essai, nous allons exécuter une simple ligne de code JavaScript dans du code Java :

public static void main(String[] args) throws Exception {
     ScriptEngineManager manager = new ScriptEngineManager();
     ScriptEngine engine = manager.getEngineByName("JavaScript");
     engine.eval("println('Mon premier script !');");
}

Rien de plus ! C'est en fait assez simple. Nous commençons par instancier un ScriptEngineManager qui nous permettra de charger le moteur de script souhaité. Nous l'utilisons sur la deuxième ligne : nous chargeons le moteur JavaScript. Aucune installation de bibliothèque nécessaire, car ce moteur est fourni avec Java 6 ! Ces deux premières lignes seront utilisées dans tous nos exemples. Nous terminons en demandant à notre moteur d'évaluer une ligne de code, ici l'affichage du texte "Mon premier script !".

Après compilation et exécution, la ligne s'affiche bien. Rien de transcendant ici, mais nous voyons que nous allons facilement pouvoir exécuter du code JavaScript.

Exemple 2 : interagir avec un script externe

Dans l'optique d'utiliser un système de plugins, il nous faut d'abord pouvoir sortir le code JavaScript du code Java, et le placer dans un fichier externe. De plus il nous faut pouvoir appeler une fonction en lui passant des paramètres, puis pouvoir récupérer le résultat obtenu.

Pour illustrer l'exemple, nous allons imaginer un programme Java qui passe à une fonction nommée compute() deux chiffres qu'elle traitera. Le code Java récupèrera le résultat du traitement effectué par compute() et l'affichera. Enfin, nous allons créer deux plugins JavaScript qui implémenteront tous les deux la fonction compute(). L'un fera une addition (plus.js) des deux chiffres donnés, l'autre une soustraction (moins.js). On commence par le code Java :

public static void main(String[] args) throws Exception {
     ScriptEngineManager manager = new ScriptEngineManager();
     ScriptEngine engine = manager.getEngineByName("JavaScript");
     engine.eval(new FileReader("plus.js"));
     Invocable inv = (Invocable) engine;
     System.out.println(inv.invokeFunction("compute",3,2));
}

Comme dans le premier exemple, nous récupérons le moteur JavaScript. Ensuite plutôt que directement évaluer du code, nous passons un fichier à la fonction eval(), qui contiendra notre code JavaScript. Nous commençons ici par plus.js, qui fera une addition. Vous pourrez ensuite utiliser moins.js. Enfin, nous invoquons la fonction compute() et lui passons deux arguments (3 et 2). Nous récupérons le résultat du traitement, qui est un Object, et qui contiendra ici un chiffre (un Double à vrai dire), nous l'affichons directement.

Voici le contenu de plus.js :

function compute(a,b) {
   return a+b;
}

Celui de moin.js :

function compute(a,b) {
   return a-b;
}

Aucun commentaire nécessaire ! Exécutez le programme avec plus.js, puis moins.js, les résultats seront 5.0 puis 1.0.

Nous voyons ici que nous pouvons très facilement avoir du code Java qui exécute une fonction dont il ne connait pas le contenu. Il en récupère le résultat sous forme d'un objet et peut le traiter. D'un autre coté, nous avons des fichiers contenant du code JavaScript qui traitent de façon différente deux arguments identiques, et renvoient le résultat. Dans une application gérant des plugins, il suffit alors que le nom du fichier JavaScript soit une variable, ou provienne d'un choix de l'utilisateur, et le tour est joué ! Attention tout de même, pour un système robuste, il est important de gérer les exceptions afin de réagir aux problèmes qui pourraient survenir.

Exemple 3 : Exporter des variables dans le script externe

Lorsque l'on mets en place un système de plugins, on souhaite que ces derniers puissent avoir accès aux informations de l'application, souvent par l'intermédiaire d'un noyau depuis lequel on accède à des variables. De cette manière le plugin agit de façon directe avec l'application, en consultant des informations, et en les mettant à jour. Nous allons dans cet exemple voir qu'il est tout à fait possible de faire cela avec Java et Rhino. Nous allons créer une classe Afficheur contenant la méthode affiche() qui se contente d'afficher dans la console une ligne de texte. Ensuite, nous allons instancier un objet de cette classe et l'exporter dans notre script JavaScript. Ce dernier utilisera dans son code l'objet exporté et appellera la méthode affiche() :

private static class Afficheur {
   public void affiche() {
       System.out.println("J'affiche !");
    }
}
public static void main(String[] args) throws Exception {
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("JavaScript");
    engine.eval(new FileReader("afficheur.js"));
    Afficheur aff = new Afficheur();
    engine.put("monAfficheur",aff);
    Invocable inv = (Invocable) engine;
    inv.invokeFunction("affiche");
}

On voit ici qu'une fois l'objet Afficheur instancié, on l'exporte dans le script grace à la méthode put(). On indique que le nom de variable par laquelle l'objet sera accessible dans le script sera "monAfficheur". Enfin on appelle la fonction affiche() implémentée dans le script afficheur.js dont voici le contenu :

function affiche() {
   monAfficheur.affiche();
}

La fonction affiche() utilise donc la variable monAfficheur disponnible grace à l'export fait depuis le code Java, et appelle la méthode affiche(). Voilà notre liaison réalisée ! Depuis notre code JavaScript, nous accédons à un objet de notre application Java et pouvons l'utiliser. Ici nous ne réalisons qu'un simple affichage, mais tout est possible. Notez qu'il est possible de définir différent scope lors de l'export des variables permettant ainsi de leur donner des valeurs différentes selon le contexte utilisé lors de l'évaluation. Plus de renseignement dans la documentation.

Exemple 4 : Implémenter une interface

Nous progressons dans les fonctionnalités proposées pour réaliser une gestion de plugins. Mais il est possible d'aller plus loin, et de définir plus précisément les fonctionnalités offertes par les plugins. Pour cela, nous pouvons définir en Java une interface, et l'implémenter depuis un plugin. Une fois le code du plugin évalué, nous appelons une méthode depuis le code Java, dont l'implémentation a été réalisée depuis le code JavaScript ! Il devient alors possible de définir proprement en Java les méthodes qui seront à implémenter par les plugins, sans en connaitre le traitement évidemment, et d'utiliser ensuite dans le code des objets instanciant cette interface. Mieux que du blabla, le code : 

private interface Afficheur2 {
    void affiche2();
}
public static void main(String[] args) throws Exception {
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("JavaScript");
    engine.eval("function affiche2() { println('affiche (2)'); }");
    Invocable inv = (Invocable) engine;
    Afficheur2 aff = inv.getInterface(Afficheur2.class);
    aff.affiche2();
}

Pour l'exemple, je n'utilise pas de fichier externe, mais directement une ligne de code JavaScript. Ce code implémente la méthode affiche2() de l'interface Afficheur2 en faisant un simple affichage de ligne. Nous créons un objet de type Afficheur2 puis récupérons l'instanciation de l'interface grâce à la méthode getInterface().

Enfin, nous utilisons ce nouvel objet et appelons la méthode affiche2() qui effectuera ce qui a été défini dans le code JavaScript, c'est à dire afficher une ligne de texte.

Nous voyons ici qu'il nous est donc possible d'écrire du code générique et de l'utiliser. C'est grâce aux plugins utilisés que instanciation est faite et nous pouvons ainsi pour un même code Java obtenir des comportement totalement différents.

Exemple 5 : Utilisation des classes Java depuis le code JavaScript

Comme dernier exemple et pour montrer toute la puissance de Rhino, nous créons un code Java qui se contentera d'évaluer un script :

public static void main(String[] args) throws Exception {
    ScriptEngineManager manager = new ScriptEngineManager();
    ScriptEngine engine = manager.getEngineByName("JavaScript");
    engine.eval(new FileReader("frame.js"));
}

Le script en revanche est particulier, car il importe directement des classes Java et utilise les objets instanciés. Le script frame.js va créer une JFrame et l'afficher. Il affichera aussi le titre de la fenêtre, qu'il récupère directement depuis la propriété de la JFrame :

importClass(javax.swing.JFrame);
importClass(java.awt.Dimension);

var frame = new JFrame("hello");
frame.setSize(new Dimension(250,250));
frame.setVisible(true);
println(frame.title);

Exécutez le programme, une fenêtre apparait !

JFrame depuis un JavaScript


Voilà pour cette petite initiation au scripting avec Java et son application avec l'utilisation de plugins. Il y aurait surement beaucoup plus à dire, mais vous disposez maintenant d'une base pour débuter, et de l'alliance du Java et du JavaScript pour vos applications ! Ce sujet aura aussi été pour moi l'occasion de reprendre la plume sur ce blog, chose qui n'avait pas été faite depuis trop longtemps !


Fabien (eponyme)

phpBB3 - redimensionner automatiquement les images trop grandes

Mehdi Bahri

Comme vous l'aurez surement remarqué si vous maintenez un forum phpbb3, le forum n'effectue par défaut aucun redimensionnement des images insérées dans les posts, ce qui peut mener à des résultats à la fois très inesthétiques et nuisibles à la lisibilité du forum pour peu que l'utilisateur insert une image trop grande.

Je propose ici une modification rapide et simple permettant de redimensionner à l'affichage les images trop grandes tout en proposant un lien vers la version pleine taille (il suffit de cliquer sur l'image). NB : Le redimensionnement se fait côté client et n'alourdit pas la charge du serveur, mais il n'est effectif qu'une fois l'image entièrement chargée.

Ouvrez le fichier includes/bbcode.php et recherchez la fonction bbcode_tpl ligne 407, vous devriez avoir à l'intérieure de la fonction l'array suivant :

$bbcode_hardtpl = array(
        'b_open'        => '<span style="font-weight: bold">',
        'b_close'       => '</span>',
        [...]
        'email' => '<a href="mailto:$1">$2</a>'
);

Modifiez la ligne correspondant au tag img comme suit :

'img' => '<a href="http://mehdinux.co.cc/index.php?post/2010/06/02/$1" title="Image en pleine taille"><img src="http://mehdinux.co.cc/index.php?post/2010/06/02/$1" alt="' . $user->lang'IMAGE' . '" onload="javascript:if(this.width > 640){ this.height = Math.round(((640)/this.width)*this.height); this.width = (640); }"/></a>'

Explication du code :

  • Je passe sur la balise a, en précisant juste que $1 est remplacé par l'adresse de l'image placée entre les balises.
  • On fait appel à l'attribut onload pour exécuter un code javascript, l'attribut onload d'un objet permet de gérer les actions liées à l'évènement "l'objet est chargé".

Le javascript :

  • On a une première condition if portant sur la valeur de l'image, this.width est un exemple d'utilisation de la POO en javascript (très basique je l'admet), this fait référence à l'objet dont la fonction en cours est une méthode. On accède ensuite à la propriété publique width qui n'est autre que la largeur de l'objet qu'on compare avec la valeur maximale fixée par vos soins en fonction de votre forum.
  • Ensuite, on calcule la nouvelle auteur de l'image redimensionnée, en prenant pour largeur la largeur maximale fixée et en conservant le rapport Hauteur/Largeur original (on évite ainsi de déformer l'image) que l'on donne à l'image via this.height.

Il est possible également d'intégrer une LightBox à l'aide de jQuery et du plugin qui va avec, cependant j'ai testé chez moi avec un template non officiel personnalisé par mes soins et je n'ai pas pu la faire marcher même en bidouillant et en refaisant la manip plusieurs fois à me référant à la doc. Le problème étant que l'image s'ouvre toujours dans une nouvelle fenêtre/un nouvel onglet en plus de l'effet lightbox et le tout passe très mal sous IE (lightbox affichée en permanence, problème d'icones etc.), bref j'ai laissé tomber. Si vous avez des idées là dessus par contre je suis preneur ! :)" class="smiley