vendredi 19 novembre 2010

Découvrir et lancer les classes d'un jar ayant une méthode "main"

Introduction

Encore une utilité de l’introspection : afficher les classes disposant d'une méthode "main" dans un jaret offrir la possibilité de la lancer. C'est bien pratique quand on ne veut pas lancer la main-class du manifest.

Méthode

  • Référencer le jar dans un classLoader
  • Parcourir les entrées du jar pour ne prendre que les classes ayant une méthode publique, statique dénommée "main"
  • Les afficher par exemple dans un ordre chronologique

Extrait de code concernant l’extraction

File f = new File(location);
loader = new URLClassLoader(new URL[] { f.toURI().toURL()  } );
JarFile jf = new JarFile(location);
Enumeration e = jf.entries();
JarEntry je = null;
for( int j=0 ; e.hasMoreElements() ; j++ )
      {
      je = (JarEntry)e.nextElement();
//---------------------------------------------
public static boolean hasPublicStaticVoidMainMethod ( Class _c )
{
      Method [] publicMethods = _c.getMethods() ;
      for ( Method uneMethode : publicMethods )
      {
            int m = uneMethode.getModifiers() ;
            if ( ( Modifier.isPublic(m) ) && ( Modifier.isStatic(m) ) && ( uneMethode.getReturnType().toString().compareTo("void") == 0 ) && ( uneMethode.getName().compareTo("main") == 0 ) )
            {
                  return true ;
            }
      }
      return false ;
}
//---------------------------------------------

Extrait de code concernant l'invocation de la méthode

Class c = Class.forName(s);
Class[] argTypes = new Class[] { String[].class };
Method main = c.getDeclaredMethod("main", argTypes);
String[] mainArgs = {} ;
main.invoke(null, (Object)mainArgs);

Copies d’écrans correspondantes


Exemple concernant rt.jar


dimanche 19 septembre 2010

Rétro-conception de Design Patterns java

Objectif

être capable de retrouver des Design Patterns éprouvés dans des classes Java.

Intérêt

Même si de nos jours les outils de rétro-conception savent très bien passer de classes java à des diagrammes UML, un pas supplémentaire peut être fait en retrouvant les Design Patterns noyés anonymement dans des schémas. Un niveau d'abstraction supplémentaire serait donc le bienvenu.

Bonnet blanc et blanc bonnet

Il existe en fait de nombreuses façons de concevoir ces Design Patterns, avec des variantes mineures, qui font tout le charme des algorithmes visant à retrouver les 3 types de DP ( construction, structuration, comportement ). Un groupe connu sous le nom de "bande des quatre" ( Gang of four en Anglais ) est cité sur le site de SUN comme étant le premier à s'être penché sur le problème.
Cependant, l'expérience a prouvé que les concepteurs de SUN n'avaient pas toujours suivi ces bonnes pratiques standard.

Cas du Pattern "Singleton"

Il nécessite :
  • un constructeur privé
  • une méthode d'instance
  • un attribut d'instance

Exemple du pattern Singleton :

public class Singleton
{
    /** Récupère l'instance unique de la class Singleton.<p>
     * Remarque : le constructeur est rendu inaccessible
     */
    //---------------------------------------------  
    public static Singleton getInstance()
    {
        if (null == instance)
        { // Premier appel
            instance = new Singleton();
        }
        return instance;
    }
    //---------------------------------------------
    /** Constructeur redéfini comme étant privé pour interdire
     * son appel et forcer à passer par la méthode <link
     */
    private Singleton()
    {
    }
    //---------------------------------------------
    /** L'instance statique */
    private static Singleton instance;
    //---------------------------------------------
}

Exemple de code pour le détecter :

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class CheckSingleton
{
    boolean debug = false ;
    //---------------------------------------------
    public CheckSingleton( String _className )
    {
        traiter(_className) ;
    }
    //---------------------------------------------
    boolean hasNoPublicConstructor ( Class _c )
    {
        boolean b = false ;
        Constructor[] publicConstructeurs = _c.getConstructors();
        if ( publicConstructeurs.length == 0 )
        {
            b = true ;
            if ( debug )
                System.out.println( "hasNoPublicConstructor" ) ;
        }
        return b ;
    }
    //---------------------------------------------
    boolean hasSomePrivateConstructor ( Class _c )
    {
        boolean b = false ;
        Constructor[] tousConstructeurs = _c.getDeclaredConstructors();
        for ( Constructor unConstructeur : tousConstructeurs )
        {
            String name = unConstructeur.getName();
            int m = unConstructeur.getModifiers() ;
            if ( Modifier.isPrivate(m) )
            {
                b = true ;
                if ( debug )
                    System.out.println( "hasSomePrivateConstructor" ) ;
            }
        }
        return b ;
    }
    //---------------------------------------------
    boolean hasSomePublicStaticDedicatedMethod ( Class _c )
    {
        boolean b = false ;
        Method [] publicMethods = _c.getMethods() ;
        for ( Method uneMethode : publicMethods )
        {
            String type = uneMethode.getReturnType().getName() ;
            int m = uneMethode.getModifiers() ;
            if (
                    ( Modifier.isPublic(m) )
                    && ( Modifier.isStatic(m) )
                    && ( _c.getName().compareTo(type) == 0 )
            )
            {
                b = true ;
                if ( debug )
                    System.out.println( "hasSomeDedicatedMethod" ) ;
            }
        }
        return b ;
    }
    //---------------------------------------------
    boolean hasSomePrivateStaticDedicatedProperty( Class _c )
    {
        boolean b = false ;
        Field [] tousChamps = _c.getDeclaredFields() ;
        for ( Field unAttribut : tousChamps )
        {
            int m = unAttribut.getModifiers() ;
            String type = unAttribut.getType().toString() ;
            type = type.replace("class ", "") ;
            //System.out.println( "type=" + type ) ;
            if (
                    (Modifier.isPrivate(m))
                    && (Modifier.isStatic(m))
                    && ( _c.getName().compareTo(type) == 0 )
            )
            {
                b = true ;
                if ( debug )
                    System.out.println( "hasSomeDedicatedProperty" ) ;
            }
        }
        return b ;
    }
    //---------------------------------------------
    void traiter ( String _className )
    {
        //System.out.println( "..." ) ;
        try
        {
            Class c = Class.forName(_className);

            if     (
                    hasNoPublicConstructor(c)
                    && hasSomePrivateConstructor(c)
                    && hasSomePublicStaticDedicatedMethod(c)
                    && hasSomePrivateStaticDedicatedProperty(c)
            )
            {
                System.out.println( "Singleton" ) ;
                System.out.println( " classe        : " + _className ) ;
            }
        }          
        catch (LinkageError ee)
        {
            System.out.println("pb pour traiter : " + _className );
        }
        catch (ClassNotFoundException e)
        {
            System.out.println("pb pour traiter : " + _className );
        }
    }
    //---------------------------------------------
}

Cas déjà traités

  • Singleton
  • Proxy ( avec variantes )
  • Observateur

Pour approfondir le sujet

jeudi 16 septembre 2010

Comment récupérer les types Jdbc et leurs libellés correspondants ?

Objectif

ne plus coder en dur dans des programmes des tableaux correspondants aux types Jdbc à traiter, mais les récupérer directement depuis la classe "java.sql.Types"

Solution

import java.lang.reflect.Field;
import java.util.Hashtable;

public class GetJdbcTypes
{
// ---------------------------------------------
Hashtable<Integer,String> lesTypes = new Hashtable<Integer,String>() ;
// ---------------------------------------------
public GetJdbcTypes() throws ClassNotFoundException, InstantiationException, IllegalAccessException
{
Class c = Class.forName("java.sql.Types");
Field[] f = c.getFields();
for(int i=0;i<f.length;++i)
{
String lib = f[i].getName() ;
Integer ii = (Integer) f[i].get(c) ;
lesTypes.put(ii, lib) ;
System.out.println( lib + " = " + ii ) ;
}
}
// ---------------------------------------------
public static void main(java.lang.String[] args)
{
try
{
GetJdbcTypes appli = new GetJdbcTypes() ;
System.out.println( "nb = " + appli.lesTypes.size() ) ;
}
catch(Exception e)
{
e.printStackTrace();
}
}
// ---------------------------------------------
}

Résultat généré

BIT = -7
TINYINT = -6
...
NCLOB = 2011
SQLXML = 2009
nb = 36

Commentaires

  • le nombre de valeurs dépend de la version java, une vingtaine en jdk 1.4, 36 en jdk 6.
  • la classe ne contient que des entiers, pas besoin de complexifier ce traitement simple.

Pour approfondir le sujet

mercredi 11 août 2010

Phylogénie graphique des Ammonites en java


Objectif

Afficher dans un JTree l’arborescence évolutive ( la phylogénie ) des ammonites avec des images afin de mieux visualiser cette taxonomie et déterminer des fossiles plus simplement.

Introduction

Collectionneurs variés, vous vous êtes souvent demandés comment représenter sous forme de graphe par exemple une taxonomie comme celle des ammonites pour pouvoir mieux déterminer vos pierres.
Voici un exemple de ce qui a été  rapidement faisable de faire à partir d’un tutorial de sun d’exploration de fichier, couplé à l’affichage d’images dans un JTree.
Les images sont à récupérer sur le web ou ailleurs et à placer dans des répertoires qui représentent la taxonomie ( classe / ordre / famille / genre / espèce par exemple ) comme l’indique le schéma ci-dessous.
Le tutorial a été simplifié en retirant le composant JSplitPane et en faisant afficher les images ( feuilles ou non ) présents dans les répertoires.

Autres outils de représentation

Il existe de nombreux outils conçus dans différents langages pour « visualiser » au sens large des arborescences hiérarchiques diverses. Cet outil n’est qu’un démonstrateur qui a l’avantage d’être très simplement mis en place et qui s’adapte à pas mal de situations.
D’autres formes de visualisations ( tableaux, graphes, structures rayonnantes, etc. ) sont aussi pertinentes dans leurs contextes pour peu que l’on puisse naviguer facilement dans les niveaux dans des arborescences complexes.
Cet outil doit être pertinent concernant les BOM ( nomenclatures ).

Schéma de l’arborescence sur disque dur

Diagramme de classe fourni par sun

 

( mon outil de rétro-conception UML ne fait pas encore d’aussi beaux diagrammes )

Copie d’écran 1

Copie d’écran 2

Pour approfondir les sujets

Un bon site français concernant les ammonites : http://www.ammonites.fr
Un autre site : http://jsdammonites.fr/

vendredi 30 juillet 2010

Rétro-conception UML de classes par introspection java


Objectif

Utiliser les mécanismes d’introspection java pour afficher un diagramme de classes UML.

Introduction

Hier je me suis demandé s’il était faisable de générer un diagramme UML à partir d’une classe de base Java. Après une nuit blanche passée à plancher sur le problème, voici la première version d’un outil développé en 24 heures pouvant se révéler fort pratique et qui va bientôt concurrencer doxygen ou umlGraph ;)

Pseudo cahier des charges

  • Traiter toutes les classes dépendantes d’une classe mère ( ou classe de base )
  • Ne pas introspecter les packages java natifs ( commençant par java, javax et sun )
  • Afficher les superClasses, les interfaces et les classes utilisées par les attributs de classe
  • Afficher un diagramme UML global au format PNG à partir de l’outil graphviz

Améliorations ( version 1 )

  • Traiter toutes les classes d’une arborescence ( package ou racine )
  • Discriminer les interfaces des classes normales
  • Mettre les superclasses au dessus des classes spécialisées

Améliorations ( version 2 )

  • Faire des sous-graphes par packages
  • Gérer en objet les éléments Graphviz
  • Paramètres gérés : afficher les détails, détailler les interfaces, afficher les superclasses

Améliorations possibles

  • Traiter des jar
  • Générer un fichier petal rose
  • Remplir un fichier de propriétés avec les informations ( quasiment déjà intégré depuis la version 0 )

Diagramme UML de l’outil ( version 0 )

Diagramme UML de l’outil ( version 1 )

Diagramme UML utilisé pour les tests

Diagrammes UML utilisés pour les tests ( version 2 )

 

Pour approfondir les sujets

samedi 17 juillet 2010

comparatif de quelques extracteurs de PDF écrits en java


Objectif

Comparer 3 extracteurs de PDF écrits en java : pdfbox 1.2.0, pdfrenderer et bfopdf.

Introduction

Toujours pour récupérer des données issues de pdf, il faut extraire des images ou plus généralement des pages à partir des pages de pdf. Pour ce faire, j’ai testé 3 solutions gratuites permettant de le faire : pdfbox 1.1.0 puis 1.2.1 puis finalement 1.2.0, pdfrenderer et un produit contraint gratuitement : bfopdf.
40 pdf téléchargés et 8 pdf générés avec PDFCreator ont été utilisés pour les tests : 40 pdf issus des sites revell et monogram représentant des notices d’instructions de maquettes plastiques, plus 7 fichiers générés par PDFCreator à partir d’un même fichier, un arc-en-ciel initialement en png,  converti en  jpg, gif, bmp 1 bit ( 2 couleurs), bmp 4 bits ( 16 couleurs ), bmp 8 bits ( 256 couleurs ) et 24 bits ( « couleurs » ), et un utilisant toutes les images.

Protocole de tests

  • Télécharger les jar depuis les sites respectifs
  • Créer un projet eclipse avec 3 packages, pour chaque package,
  • Créer une classe qui liste les pdf du répertoire en question et qui appelle une classe ( thread ) d’extraction des pages ( png, rgb ou argb, 72 dpi ) généralement trouvée dans les cookbooks, tutoriaux et exemples. Ceci est dû à la non-gestion des exceptions java dans certains cas.
  • Analyser les résultats qualitativement et quantitativement selon le tableau ci-dessous

Critère
Pdfbox 1.2.0
pdfrenderer
Bfopdf 2.1.11
Observation
Vitesse d’exécution
19
11
10
Minutes
Pages générées
491
433
503
Certaines l’étant partiellement suite aux cas non gérés dans la norme pdf ( ps compressé ) ou des exceptions
Précision des dessins



Bfopdf ne génère qu’en 72 dpi, pdfbox et pdfrenderer, paramétrables,  ont été contraints à ce facteur limitant
Png ok
Ok
Ok
Ok

Jpg ok
Ok
Ok
Ok

Gif ok

Ok
Ok

Bmp 2 couleurs

Ok
Ok

Bmp 16 couleurs

Ok
Ok

Bmp 256 couleurs

Ok
Ok

Bmp couleurs
Ok
Ok
Ok

Taille d’une page de référence sortie en png
72
177
153
Ko ( en rgb ou en argb )
Argb = rgb + luminosité
Page 11 de la notice du t-80 ud référencé 03078.pdf chez revell
595 * 842 pixels
Qualité des textes
moyenne
ok
ok
En 72 dpi. Ils sont mieux écrits en 98 dpi.
Qualité des couleurs
Contrastés
Grillage trop contrasté
Trop contrasté manque de détails
Grillage monochrome noir
Manque de contraste
Grillage acceptable
Pour les détails, voir comment ressort le grillage du moteur à l’arrière de la tourelle, le grillage ressort différemment
Utilisabilité de l’image
ok
ok
Limitée …
Un gros « DEMO » rose s’affiche en surimpression avec la version gratuite. A traiter pour l’enlever. A voir pour le faire.
Rendu des dessins incomplets
Pavés noirs, images en négatif, des pixels défectueux
Pages vierges, dessins absents
R.A.S !

Sources fournis
oui
oui
Non

Verbosité des messages d’erreurs
très verbeux
Assez peu verbeux
Peu verbeux
Souvent les messages annoncent les fonctions PS non gérées ou les traces d’erreur java

Quelques compléments

  • La dernière version stable de pdfbox au moment de l’écriture de ce billet et buggée. Elle est sensée corrigée la version 1.2.0 pour du texte mais elle restitue nettement moins d’images, d'où l’utilisation de l’avant dernière version.
  • 72 dpi est la définition sur Mac, les PC utilisent 98 dpi, et pdfbox est bien meilleur avec une plus grande définition.
  • Bfopdf indique sur son site que le tiff en 200 dpi monochrome est excellent pour être faxé
  • Pdfrenderer ne fournit pas de n° de version pour ses jars. Au mieux, nous avons accès au svn de l’application.
  • Il faut utiliser une poignée de jar externes en plus de pdfbox lui-même pour utiliser les classes. Il y a peut être une incompatibilité de versions qui expliquerait les défaillances de pdfbox 1.2.1
  • Pdfrenderer, qui ne fait que visualiser des pdf, peut être utilisée avec des applets avec l’api Graphics2D.

Pour approfondir les sujets

Pdf guide de référence : http://www.adobe.com/devnet/pdf/pdfs/PDFReference.pdf ( entre autre pour le codage des opérateurs )

pdfbox
pdfrenderer
bfopdf