jeudi 11 octobre 2012

Rechercher un champs dans un des formulaires de la page

Il y a peu, on m'a demander d'aider quelqu'un a faire un code JavaScript pour rechercher un champs dans un formulaire.
Il fallait aussi que le code puisse renvoyer le formulaire si le nom cherché était le nom d'un formulaire.

Voici un exemple :
// Recherche par le nom donc l'element chercher est un input
function rechercherDansFormulaire(nomChamp) {                
    // Fonction qui affiche le nom de tous les formulaires
    // et de tous les les éléments d'un document HTML
    // Boucle tous les formulaire
    for (var i = 0; i < document.forms.length; i++) {
        // S'il s'agit d'un formulaire
        if (nomChamp == document.forms[i].name) {
            return document.forms[i] ;
        }
        
        // Boucle tous les éléments du formulaire i
        for (var l = 0; l < document.forms[i].elements.length; l++) {
            if (nomChamp == document.forms[i].elements[l].name) {
                return document.forms[i].elements[l] ;
            }
        }
    }

    return null ;
}

mercredi 3 octobre 2012

Personnaliser le filtre d'un JFileChooser

Afin de pouvoir personnaliser les filtres dans un JFileChooser, il est nécessaire de créer une classe héritant dans FileFilter.
Cette classe va retourner une description qui sera affichée dans la boite de dialogue en bas et surtout la méthode accept() qui va indiquer au JFileChooser s'il faut prendre en compte (et donc afficher dans la boite de dialogue) le fichier.
import java.io.File;

import javax.swing.filechooser.FileFilter;


public class FileExtension extends FileFilter {
 private String extension ;
 
 private String description ;
 
 public FileExtension(final String extension, final String description)
 {
  if (extension != null)
  {
   this.extension = extension.toLowerCase() ;
  }
  
  this.description = description ;
 }
 
 @Override
 public boolean accept(File f) {
  return (testFile(f.getAbsolutePath()) || f.isDirectory()) ;
 }

 @Override
 public String getDescription() {
  return description ;
 }

 private boolean testFile(final String fileName)
 {
  return ((extension != null) && (!"".equals(extension)) &&
    fileName.toLowerCase().endsWith(extension)) ;
 }
}
Ensuite, cette objet doit être ajouter à la liste des filtres du JFileChooser :
//Create a file chooser
fc = new JFileChooser() ;

// Delete previous filter (by default *.*)
fc.resetChoosableFileFilters();
fc.addChoosableFileFilter(new FileExtension("*.exe", "Executable file"));

lundi 1 octobre 2012

Les subtilités des versions JBoss

Voici un retour d'expérience sur une migration JBoss 3.5 vers 4.3.
Je n'avais encore jamais participé à aucune migration de ce type.
Nous avions fait des tests de chaque module et tout semblait correct. Pas de modification spécifique en vue.
Erreur !

Lors du test d'intégration, nous avons donc mis plusieurs WAR dans la même instance JBoss.
Ces modules utilisaient les mêmes librairies mais les embarquaient chacune dans leur WAR.
En effet, ces librairies contenaient des singletons propre à chaque application.

Phénomène étrange, les WAR semblaient partager le même classloader.

En effet, il n'est possible d'indiquer à JBoss, de ne pas partager le même classloader (java2ParentDelegation = false) que pour les EAR, dans sa configuration générale.
Pourtant, en JBoss 3.5 cela fonctionnait, alors que se passe-t-il ?

Du côté du fichier jboss-web.xml qui embarqué dans chaque WAR permet de configurer le comportement JBoss, tout semblait correct :
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE jboss-web PUBLIC "-//JBoss//DTD Web Application 2.3//EN"
"http://www.jboss.org/j2ee/dtd/jboss-web_3_2.dtd">

<jboss-web>
 <class-loading>
  <loader-repository>
   nom_war:loader=nom.war
   <loader-repository-config>
    java2ParentDelegation=false
   </loader-repository-config>
  </loader-repository>
 </class-loading>

</jboss-web>
Après, une plongée dans la documentation de JBoss, un commentaire me met la puce à l'oreille.
Ce commentaire indique une particularité de JBoss 4.2.x à 5.0.0.

Le format que fichier à changé, pour ces versions, il faut :
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE jboss-web PUBLIC "-//JBoss//DTD Web Application 2.3//EN"
"http://www.jboss.org/j2ee/dtd/jboss-web_4_2.dtd">

<jboss-web>

   <!-- Fonctionne uniquement avec JBoss de 4.2.x a 5.0.0 -->
   <loader-repository> 
      nom_war:archive=nom.war
   <loader-repository-config>
    java2ParentDelegation=false
   </loader-repository-config>
   </loader-repository> 

</jboss-web>
Attention, à partir de la version 6, on revient comme en 3.5 !

lundi 24 septembre 2012

Maven, des phases et des goals

De nombreux utilisateurs de maven ne comprennent pas bien l'esprit maven.
En effet, Maven ne se comporte pas comme du Ant ou un Makefile.

Maven, il faut bien l'avouer, n'a rien inventé. Il s'est inspiré de la vie. Encore fallait-il y penser.
Un produit (jar, war, un téléphone...) est régit par la loi de la nature. Il y a un cycle. De la naissance à la retraite (on va s'arrêter là c'est mieux).

Suivant le produit, le cycle de vie n'est pas le même. Maven définit des produits standard, à savoir des packaging : pom, jar, maven-plugin, ejb, war, ear, rar, par.
Tous c'est package ont le même cycle de vie.

Un cycle de vie c'est quoi ?
Toujours comme la vrai vie. Un être humain sera : foetus, bébé, enfant, adolescent, adulte, retraité.
Ce sont les phases de la vie.
Un jar aura donc comme phases dans son cycle de vie :
validate
initialize
generate-sources
process-sources
generate-resources
process-resources
compile
process-classes
generate-test-sources
process-test-sources
generate-test-resources
process-test-resources
test-compile
process-test-classes
test
prepare-package
package
pre-integration-test
integration-test
post-integration-test
verify
install
deploy
D'abord, il y a validation de la structure du pom.xml, puis initialisation...

Et comme dans la vrai vie, il y a dans chaque phase, des étapes appelées goal dans maven.
Je pense qu'une des incompréhension vient d'une mauvaise traduction du mot goal (ce n'est pas un but).

Si on prend un être humain, dans la phase adulte, il passe son permit voiture, achète une voiture, s'installe en couple, se marie, achète une maison... Ce sont bien des étapes.
Les étapes (goal) sont dans un ordre bien définit. Cet ordre est lui aussi définit dans le cycle de vie.

Peut-on redéfinir un cycle de vie ?
Oui, pour cela, il faut créer un nouveau packaging (exemple zip). Mais là, c'est une autre histoire.

Donc, si on résume :
Un cycle de vie contient des phases, qui elles même contiennent des goals.

Une chose à se rappeler, si on lance un plugin dans un goal donné (qui donc appartient à une phase), toutes les phases et goals se situant avant seront exécutés.

Il faut quand même savoir qu'il est possible d'avoir des goals non attachés à une phase. Mais bon, c'est pour plus tard.

lundi 17 septembre 2012

Changer de schéma par défaut sous Oracle

Il existe deux méthodes d'accès aux tables dans une requête SQL :

  • sans préfixe : “select * from table” : pour les tables du schéma de l'utilisateur
  • avec préfixe : “select * from autre_schema.table” : pour les tables d'un autre schéma (sur lequel l'utilisateur dispose des bons droits)

Lors de chaque connexion à une base de données Oracle, un utilisateur est donc affecté au schéma qui porte le même nom que son identifiant de connexion. Les accès aux tables sans préfixes (“from table”) sont alors effectués dans ce schéma par défaut.

Description du processus coté Oracle :

  • Connexion avec l'utilisateur TOTO
  • Affectation du schéma TOTO comme “current_schema” de la session
  • Réception d'une requpete “select * from table” qui est transformée en “select * from TOTO.table”

Problème : si on se connecte avec l'utilisateur “system”, et que l'on souhaite faire une requête sur les données de TOTO, on doit ajouter le préfixe correspondant au schéma devant chaque accès aux tables. Ceci peut s'avérer très long et risqué sur des requêtes complexes.

Solution : il est possible changer, pour la session courante, de “current_schema”. Après la connexion, il suffit d'exécuter la requête
alter session set current_schema=<IDENTIFIANT_DU_SCHEMA_CIBLE>
pour que les accès sans préfixes soient fait sur le schéma <IDENTIFIANT_DU_SCHEMA_CIBLE>.

Merci à Drazzib pour l'astuce.

vendredi 7 septembre 2012

Créer dynamiquement une image en Java

Il y a peu, j'ai voulu créer un ripper d'image du jeu mythique des années 90, "Jill of the Jungle".
J'ai trouvé sur le site www.shikadi.net une explication sommaire des fichiers d'image.
Il me fallait donc créer une image dynamiquement et l'enregistrer.

J'ai cherché de la documentation à ce sujet. J'ai trouvé l’officielle d'Oracle, très sommaire, et des exemples sur internet un peu trop complexe.

L'idée est simple : créer une image, dessiner un pixel et l'enregistrer.

Voici l'exemple ci-dessous.
BufferedImage image =
    new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB) ;

Graphics2D g2 = image.createGraphics() ;
  
BufferedImage image =
    new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB) ;

Graphics2D g2 = image.createGraphics() ; 

g2.setColor(color) ;
                  
g2.drawLine(x, y, x, y) ;

outputfile = new File(fileName) ;
ImageIO.write(bi, "png", outputfile);
Création de l'image (un BufferImage puisque tout est en mémoire), de type Alpha (transparence), red, green, blue.
Création d'un graphic (objet permettant le dessin).
Définition de la couleur du pinceau.
Dessin du pixel.

Graphic2D ne permet pas de dessiner un pixel, via une méthode. On dessine donc une ligne, d'un point.

A la fin, est utilisé la classe ImageIO pour enregister le buffer.

C'est aussi simple que ça

lundi 27 août 2012

Les nombres non signés en Java

Voici la liste des types primitifs en Java

TypeSignificationTaille (en octets)Plage de valeurs acceptées
charCaractère Unicode2'\u0000' → '\uffff' (0 à 65535)
byteEntier très court1-128 → +127
shortEntier court2-32 768 → +32 767
intEntier4-231≈-2,147×109 → +231-1≈2,147×109
longEntier long8-263≈-9,223×1018 → +263-1≈9,223×1018
floatNombre réel simple4±2-149≈1.4×10-45 → ±2128-2104≈3.4×1038
doubleNombre réel double8±2-1074≈4,9×10-324 → ±21024-2971≈1,8×10308
booleanValeur logique (booléen)1true (vrai), ou false (faux)

Ce que l'on constate, c'est qu'il n'y a que des nombres non signé.
Si vous lisez un fichier binaire et que vous récupérer un octet (byte), naturellement, vous vous attendez à un nombre non signé.

Mais, voilà, Java ne vois pas les choses de la sorte.
Alors comment avoir la valeur d'un byte non signé ?
L'astuce est plutôt simple quoique déconcertante :
final DataInputStream file = ...
int byte0 = file.readByte() & 0xff ;
En appliquant un masque 0xff et en le mettant dans un int, on force Java à ne pas interpréter le contenu du byte.
Exemple, si le byte = -127, le signe disparait et la valeur voulue (255) est récupérée.