Approfondissement du Java X. BLANC & J. DANIEL Xavier.Blanc@lip6.fr, Jerome.Daniel@der.edf.fr

Preview:

Citation preview

Approfondissement du JavaApprofondissement du Java

X. BLANC & J. DANIELXavier.Blanc@lip6.fr , Jerome.Daniel@der.edf.fr

Plan du coursPlan du cours

• Compléments sur la langage Java

• L'API Reflexion

• Le chargement de classes

• La sécurité dans Java

• Les interfaces natives

Compléments sur la langage JavaCompléments sur la langage Java

Approfondissement de JavaX.BLANC & J. DANIEL

Commenter ses programmes JavaCommenter ses programmes Java

• Pour commenter les programmes Java, on peut utiliser le système « javadoc » qui permet par la suite la génération automatique de documentations.

• Format d'un commentaire « javadoc » :

/** * Placer ici le commentaire JavaDoc */

Le début du commentaireest obligatoirement /**

Les « tags » de JavaDocLes « tags » de JavaDoc

• Les « tags » identifient des sections afin de structurer le commentaire.

• Les « tags » disponibles sont :– author

– version

– param

– return

– exception

– see

– deprecated

• Chaque « tag » respecte la syntaxe suivante :

@tag valeur

Génération des documentationsGénération des documentations

• L'utilitaire « javadoc » permet de générer les documentations.

javadoc [ options ] fichiers sources

• La génération des documentations peut être personnalisée à l'aide de « doclet ».

– mise à disposition d'une API permettant de récupérer les informations de commentaires,

– utilisation de l'option « -doclet »

Les attentes de signauxLes attentes de signaux

• L'instruction « wait » bloque le thread courant.

public void wait();public void wait( long timeout );

• L'instruction « notify » débloque un thread en attente.

public void notify();

• L'instruction « notifyAll » débloque tous les threads qui sont en attentes.

public void notifyAll();

L'objet sur lequel s'applique ces opérations d'attentes est appelé un moniteur.

Utilisation des moniteursUtilisation des moniteurs

• Utilisez la notion de moniteur pour développer des opérations substituants « suspend » et « resume ».

Le clonageLe clonage

• Le clonage permet la copie d'un objet java.

• Pour pouvoir être cloné, un objet doit implanter l'interface « java.lang.Cloneable ».

• La méthode « clone » de la classe objet permet le clonage d'un objet champs par champs.

• Les champs statiques ne sont pas clonés.

Si un objet clonable contient un autre objet clonable, celui-ci ne sera pas automatiquement cloné.

Gestion des processusGestion des processus

• Avec Java, on peut gérer les processus :– création et exécution de processus,

– arrêt de processus

• Deux classes importantes sont à utiliser :– java.lang.Runtime

– java.lang.Process

• On récupère le Runtime via l'opération statique « getRuntime » de l'interface Runtime :

static public Runtime getRuntime();

La classe « java.lang.Runtime »La classe « java.lang.Runtime »

• Cette classe permet entre autre d'obtenir des informations sur l'environnement d'exécution :– mémoire disponible : freeMemory

– mémoire totale : totalMemory

• On peut également créer de nouveau processus :

Process exec( String command )

throws IOException

Process exec( String command, String [] envp )

throws IOException

La classe « java.lang.Process »La classe « java.lang.Process »

• La classe Process symbolise un processus. Les opérations disponibles sont :– void destroy() : supprime le processus

– int waitFor() : suspend l'exécution du thread courant jusqu'à la fin de ce processus ( avec exception possible : IllegalThreadStateException )

– int exitStatus(): retourne le code d'arrêt du processus

• On peut aussi accéder aux flux d'entrées et de sorties du processus :– InputStream getInputStream( )

– OutputStream getOutputStream( )

– InputStream getErrorStream( )

Utilisation des processusUtilisation des processus

• Mettre en place une application qui exécute trois processus fils ou chaque fils effectue une simple attente de 10 secondes.

• Si au bout de 30 secondes, les processus fils ne sont pas terminés, le père doit tuer tous ses fils encore vivants.

L'API ReflexionL'API Reflexion

Approfondissement de JavaX.BLANC & J. DANIEL

Une API réflexive !Une API réflexive !

• Java permet d'obtenir des informations de descriptions sur les classes, les opérations, les types, etc…..

• Grâce à la réflexion, on peut analyser dynamiquement un classe, mais également l'utiliser.

• Offre une grande souplesse d'utilisation des applications java telle que la mobilité du code.

Obtenir la description d'une classe JavaObtenir la description d'une classe Java

• Une classe est symbolisée par la classe « java.lang.Class »

• Chaque instance de classe peut retourner un descripteur de sa classe par l'intermédiaire de l'opération « getClass »

maClasse m = new maClasse();

java.lang.Class cl = m.getClass();

• Chaque classe comporte également un attribut appelé « class » pour récupérer sa description :

java.lang.Class cl = maClass.class;

Obtention dynamique d'une description !Obtention dynamique d'une description !

• A partir du nom d'une classe, on peut récupérer dynamiquement sa

description en utilisant l'opération statique « forName » :

java.lang.Class cl = java.lang.Class.forName("exemple.maClasse");

• Attention, cette opération retourne l'exception

« ClassNotFoundException »

La classe « java.lang.Class »La classe « java.lang.Class »

• La classe « Class » retourne toutes les informations permettant de

connaître la description d'une classe :

– son nom ( getName ),

– son package ( getPackage ) ,

– ses constructeurs ( getConstructors ),

– ses méthodes ( getMethods ),

– ses attributs ( getFields ),

– son ancêtre ( getSuperclass ) ,

– les interfaces qu'elle implante ( getInterfaces ).

Description d'un PackageDescription d'un Package

• La description d'un package est offert par la classe « java.lang.Package » au moyen de l'opération « getPackage »

java.lang.Package getPackage();

• Cette classe propose les opérations suivantes :

– String getName() : retourne le nom du package

– Package getPackage() : retourne le package de ce package

– Package [] getPackages() : retourne la liste des packages de ce

package.

Description des constructeursDescription des constructeurs

• Pour décrire un constructeur, on utilise classe « java.lang.reflect.Constructor ». Les descriptions de constructeurs sont retournés par « getConstructors » :

java.lang.reflect.Constructor [ ] getConstructors();

• Les opérations de « java.lang.reflect.Constructor » sont :– String getName() : retourne le nom du constructeur

– int getModifiers() : retourne les modificateurs qui s'appliquent au constructeur

– java.lang.Class getExceptionType() : retourne les descriptions des exceptions lancées par le constructeur

Les modificateursLes modificateurs

• La classe « java.lang.reflect.Modifier » permet la manipulation des modificateurs retournés par les descriptions.

• Cette classe propose un ensemble d'opérations permettant de tester la validité d'un modificateur ( final, abstract, public, private, protected, static, transient, synchronized ). Ces opérations respectent la syntaxe suivante :

public static boolean isXXXXX( int modifier )

Description des méthodesDescription des méthodes

• Afin de décrire une méthode, la classe « java.lang.reflect.Method » est utilisée. Les descriptions de méthodes sont retournées par « getMethods »

java.lang.reflect.Method [ ] getMethods( );

• Les principales opérations de cette classe sont :– String getName() : retourne le nom de la méthode

– int getModifier() : retourne les modificateurs de la méthodes

– Class [] getExceptionType() : la liste des exceptions

– Class [] getParamType() : la liste des paramètres

– Class getReturnType() : retourne le type de retour de la méthode

Description des attributsDescription des attributs

• Pour décrire un attribut, la classe « java.lang.reflect.Field » est utilisée.

Java.lang.reflect.Field [ ] getFields();

• Les principales opérations sont :– String getName() : nom de l'attribut

– int getModifier() : modificateur de l'attribut

– Class getType() : type de l'attribut

• Pour savoir, si un type est un type « primitif », on utilise l'opération « isPrimitive ».

Description des interfacesDescription des interfaces

• Afin de décrire une interface, on emploi la classe

« java.lang.Class » !

java.lang.Class [ ] getInterfaces();

• Pour savoir si une classe « Class » décrie une interface, on utilise

l'opération « isInterface ».

Description des tableauxDescription des tableaux

• Une classe « Class » peut décrire un tableau. Pour savoir, si c'est un tableau on utilise l'opération « isArray »

• Pour connaître le type du tableau, on emploi l'opération

« getComponentType »

if ( cl.isArray() ){

java.lang.Class type = cl.getComponentType();}

Création dynamique d'une instance de classe !Création dynamique d'une instance de classe !

• On peut créer dynamiquement une instance de classe à l'aide de l'opération « newInstance » :

try{

java.lang.Class clz = java.lang.Class.forName("maClasse");

java.lang.Object obj = clz.newInstance();

maClasse maC = ( maClasse ) obj;}catch ( java.lang.Exception ex ){

ex.printStackTrace( );}

La classe à instancier doit impérativement comporter un constructeur sans paramètre.

Utilisation dynamique de constructeursUtilisation dynamique de constructeurs

• Si une classe comporte un constructeur avec paramètres, on doit

utiliser l'opération « newInstance » de la classe « Constructor »

afin d'en créer une instance :

java.lang.Object newInstance ( java.lang.Object [ ] params )

Utilisation dynamique d'une classe !Utilisation dynamique d'une classe !

• L'API reflexion permet une utilisation dynamique des classes :– auto-description,

– instanciation à la demande.

• Il est impératif de pouvoir effectuer des invocations dynamiques sans utiliser directement le type de la classe cible.

java.lang.Class clz = java.lang.Class.forName("maClasse");

java.lang.Object obj = clz.newInstance();

maClasse maC = ( maClasse ) obj;

Appel dynamique d'une opérationAppel dynamique d'une opération

• Pour appeler dynamiquement une opération, on emploi « invoke » :

java.lang.Object invoke ( java.lang.Object cible, java.lang.Object [] params )

• Si une exception se produit lors de l'invocation, l'opération « invoke » génère l'exception « java.lang.reflect.InvocationTargetException »

• Parmi les opérations de cette exception, on a :– void printStackTrace()

– Throwable getTargetException()

Utilisation dynamique d'un attributUtilisation dynamique d'un attribut

• La classe « Field » comporte plusieurs opérations permettant l'accès aux valeurs des attributs.

• Ainsi, les opérations suivantes sont définies pour les types primitifs :

void setXXXX( java.lang.Object cible, XXXXX value );XXXX getXXXX( java.lang.Object cible );

• Pour les types complexes on a:

void set( java.lang.Object cible, java.lang.Object value );java.lang.Object get( java.lang.Object cible );

Mise à l'épreuve...Mise à l'épreuve...

• Développez une méthode qui décrie une classe :

– affiche ses constructeurs

– affiche ses méthodes

– affiche ses attributs.

Le chargement de classesLe chargement de classes

Approfondissement de JavaX.BLANC & J. DANIEL

Notion de « ClassLoader »Notion de « ClassLoader »

• Pour charger les classes, la machine virtuelle utilise un « ClassLoader ».

• L'utilisateur peut définir son propre chargeur de classes en surchargeant le chargeur standard.

• La classe « java.lang.ClassLoader » symbolise le chargeur de classes.

• Pour connaître le chargeur d'une classe, on emploi la méthode « getClassLoader » de la classe « Class »

La méthode « getClassLoader » renvoie NULL avec le JDK 1.1.x si le chargeur utilisé n'est pas un chargeur utilisateur.

Le chargeur systèmeLe chargeur système

• La machine virtuelle permet de récupérer une référence vers le chargeur du système.

• Pour cela, on utilise la méthode statique « getSystemClassLoader » de la classe « ClassLoader » :

public static java.lang.ClassLoader getSystemClassLoader();

Cette méthode peut éventuellement renvoyer NULL !

Fonctionnement d'un chargeur de classeFonctionnement d'un chargeur de classe

JVM

Application Java

Chargement des classesconstituant l'application

Chargeur

Classe JavaClasse Java

Classe JavaClasse Java

Chargeur système Délégation duchargement

Chargementdirect

Classe Java

Ecrire un chargeur de classe !Ecrire un chargeur de classe !

• Pour écrire son propre chargeur de classe on doit :– hériter de la classe « java.lang.ClassLoader »

– Surcharger la méthode « findClass »

public java.lang.Class findClass( String name );

• Enfin, la méthode « findClass » doit :– récupérer le code correspondant à la classe,

– définir une classe ( passage du byte code à la classe ). Pour ce faire, on utilise la méthode « defineClass » :

protected final java.lang.Class defineClass( String name, byte [] b, int off, int len );

La méthode « defineClass » ne doit jamais être appelée deux fois pour la même classe.

Remarque sur le chargeurRemarque sur le chargeur

• Lorsqu'une classe est chargée à partir d'un chargeur utilisateur, toutes les classes qu'elle utilise seront chargées par ce même chargeur.

• Si le système demande le chargement de deux fois la même classe, on peut vérifier que cela n'a pas été déjà fait par :

protected final java.lang.Class findLoadedClass( String name );

• Pour pouvoir utiliser une classe chargée, on doit la résoudre :

protected final void resolveClass( java.lang.Class class );

La classe « URLClassLoader »La classe « URLClassLoader »

• Le JDK 1.2 propose un chargeur standard symbolisé par la classe « java.net.URLClassLoader » pour charger des classes à partir de sites spécifiés via des URLs.

URLClassLoader( String [ ] URLs );

• Parmi les méthodes les plus utilisées de cette classe, on distingue :

– void addURL( String )

– static URLClassLoader newInstance( String [] URLs )

• Pour déclencher le chargement d'une classe, on doit pour tous les chargeurs appeler la méthode « loadClass »

public java.lang.Class loadClass( String name ) throws ClassNotFoundException;

A vous...A vous...

• Ecrire son propre chargeur de classes en récupérant depuis un fichier de propriétés le nom du répertoire où se trouvent les fichiers sources.

La sécurité dans JavaLa sécurité dans Java

Approfondissement de JavaX.BLANC & J. DANIEL

La sécurité dans JavaLa sécurité dans Java

• Les objectifs de la sécurité sont :

– Fournir un environnement Java sécurisé de telle sorte que les

exécutions des applications java s’effectuent d’une manière

sécurisée.

– Fournir un ensemble de mécanismes pour une utilisation à

grande échelle permettant un contrôle de la sécurité.

Le modèle du JDK 1.0Le modèle du JDK 1.0

• Le modèle de sécurité du JDK 1.0 est connu sous le nom de « sandbox »

Ressources ( fichiers, imprimantes, etc… )

J V M

sandbox

Code distantCode local

Accès très restreint pour le code distant ( Applet )

Le modèle du JDK 1.1Le modèle du JDK 1.1

• Le JDK 1.1 introduit la notion « d’applet signée » qui est l ’association d ’un code distant ( Applet ) avec une signature.

Ressources ( fichiers, imprimantes, etc… )

J V M

sandbox

Code distantCode local

Code distant signé

Le code signé est utilisé comme un code local : trusted

Le modèle du JDK 1.2Le modèle du JDK 1.2

• Dans le JDK 1.2, les applications s’exécutent selon des règles ( policy ) énumérés extérieurement.

Ressources ( fichiers, imprimantes, etc… )

J V M

sandbox

Code local ou distant ( signé ou pas )

loaderRègles

Une gestion très fine de la sécurité.

Classes, Domaines et PermissionsClasses, Domaines et Permissions

• Une permission est un droit d’accès également appelé privilège.

• Un domaine est une entité soumis à plusieurs permissions.

• Un domaine regroupe plusieurs classes. Chaque classe du domaine

profite alors des permissions de ce domaine.

– Domaines des applications

– Domaines systèmes

Gestion des permissionsGestion des permissions

• Une classe de permission permet d’avoir accès à une ressource du système.

• La classe « java.security.Permission » correspond à la classe de base de toutes les permissions Java.

• Les permissions peuvent également être manipulées sous forme d’un ensemble ( de permissions hétérogènes ) en utilisant la classe « java.security.Permissions »

Quelques types de permissionsQuelques types de permissions

• Permissions pour accéder à un fichier

java.io.FilePermission

• Permissions pour utiliser les sockets

java.net.SocketPermission

• Permissions pour utiliser les propriétés

java.util.PropertyPermission

• Permissions pour utiliser le Runtime

java.lang.RuntimePermission

• Permissions sur l’API reflexion

java.lang.reflect.ReflectPermission

• Permissions sur la sécurité

java.security.SecurityPermission

Les permissions sur les fichiersLes permissions sur les fichiers

• La classe « java.io.FilePermission » accorde les droits suivants :– lecture : read

– écriture : write

– suppression :delete

– exécution : execute

• Création d'une permission pour les fichiers :java.io.FilePermission( chemin , droits )

• Exemple :java.io.FilePermission perm =

new java.io.FilePermission("/tmp/*", "read, write");

Contrôle d’accèsContrôle d’accès

• Pour gérer les permission, on utilise un contrôleur d'accès symbolisé par la classe « java.security.AccessController »

• Pour vérifier une permission, on utilise l'opération « checkPermission »

java.io.FilePermission perm = …

AccessController.checkPermission( perm );

• On peut aussi exécuter une opération selon un privilège spéciale. On appel une telle opération : une action privilégiée.

Les actions privilégiéesLes actions privilégiées

• Une action privilégiée est un ensemble de code implantant l'interface « java.security.PrivilegedAction »

interface PrivilegedAction

{

Object run( );

}

• Pour lancer une action privilégiée, on utilise l'opération « doPrivilege » du contrôleur d'accès

Object doPrivileged( PrivilegedAction action )

Le gestionnaire de sécuritéLe gestionnaire de sécurité

• Un environnement Java est lié à un gestionnaire de sécurité symbolisé par la classe « java.lang.SecurityManager ».

• Le gestionnaire de sécurité utilise un contrôleur d'accès, ce qui permet de vérifier la validité d'une permission.

ClassLoader loader = this.getClass().getClassLoader();

if (loader != null)

{

SecurityManager security = System.getSecurityManager();

if (security != null) {

security.checkRead("path/file"); }

}

• On peut surcharger le gestionnaire de sécurité pour fournir sa propre gestion de la sécurité.

Les fichiers de permissionsLes fichiers de permissions

• On peut à l ’aide d ’un fichier de « policy » énumérer plusieurs permissions :

grant [ SignedBy xxxx ]

{

permission permission_class_name [ options ] ;

// autres permissions...

}

• Exemple :

grant

{

permission java.io.FilePermission "/tmp/*", "read, write";

}

Chargement sécurisé de classesChargement sécurisé de classes

• En définissant ses propres chargeurs de classes, l’utilisateur peut

violer la sécurité de l’environnement.

• On peut pour résoudre ce problème, hériter de la classe

« java.security.SecureClassLoader » pour définir ses propres

chargeurs.

– Utilisation des permissions,

– Plus grandes libertés dans le cas d’Applet

Epilogue sur la sécuritéEpilogue sur la sécurité

• Spécification de paramètres de sécurité depuis la ligne de

commande :

– java.security.manager

– java.security.policy

• Le JDK 1.2 augmente considérablement la gestion de la sécurité

dans Java. Mais certains travaux restent à mettre en place comme

la gestion de l'identité et de l'authentification.

Les interfaces nativesLes interfaces natives

Approfondissement de JavaX.BLANC & J. DANIEL

Utiliser depuis Java un code non Java !Utiliser depuis Java un code non Java !

• Pour des besoins spécifiques, il est parfois indispensable d'ajouter des éléments natifs aux diverses classes Java.

• Par exemple, on peut sous Windows imaginer une classe qui permette de récupérer la valeur d'une variable de l'environnement Windows.

• Pour cela, on doit faire appel à des méthodes natives. Plusieurs interfaces natives ont été définies pour pouvoir utiliser depuis Java du code non Java.

Signaler qu'une méthode est nativeSignaler qu'une méthode est native

• Pour indiquer depuis Java qu'une méthode est native, on doit tout de même la déclarer et utiliser le modificateur « native ».

• Exemple :public native void printMessage( String message );

• On doit également demander à Java de charger la librairie qui contient le code de cette méthode native. On utilise pour cela l'opération « loadLibrary » de la classe « System ».

static{ System.loadLibrary("Exemple");}

La librairie est une librairie partagée ( DLL sous Windows ) qui doit être placée dans un répertoire accessible depuis le PATH

JNI : Java Native InterfaceJNI : Java Native Interface

• JNI à été ajoutée au JDK 1.1 pour permettre l'appel de méthode

native depuis Java.

• L'utilitaire « javah -jni xxxx » permet la conception d'un header C

pour la définition du code natif.

• JNI fournie une API permettant de mettre en place un wrapper

pour interagir entre le C et les différents types et objets de Java.

Les difficultés de JNILes difficultés de JNI

• On doit tout d'abord utiliser l'API de JNI pour accéder à tous les éléments d'un objet Java.

• L'écriture d'une application complexe vient tout de suite très laborieuse à mettre en place.

• Se méfier des accès concurrents dues à plusieurs threads Java.

• Interactions complexes avec le garbage collector !

J/DirectJ/Direct

• J/Direct est une interface native proposée par Microsoft, qui

permet un accès facile aux opérations de l'API Win 32.

• On peut également utiliser J/Direct pour appeler des opérations

depuis d'autres DLL.

• Ce mécanisme est plus lent que JNI ou RNI mais s'avère très

simple à mettre en œuvre et ne requiert pas d'adaptation (wrapper)

entre les deux environnements.

Utilisation de J/DirectUtilisation de J/Direct

• Dans J/Direct, on utilise un commentaire Javadoc spécial pour importer une librairie ( DLL ) : @dll.import

/** * @dll.import("Exemple") */private native void printMessage(String text );

@dll.import peut également s'appliquer à toute une classe en faisantprécéder la déclaration de celle-ci par le commentaire adéquat.

RNI ( Raw Native Interface )RNI ( Raw Native Interface )

• RNI est également une extension apportée par Microsoft pour effectuer des appels natifs mais cette fois-ci, c'est au développeur de l'interface de gérer la phase de garbage collection.

• RNI s'utilise sur le même principe que JNI et fournie essentiellement les même possibilités.

• Par exemple, on doit utiliser « msjavah » à la place de « javah » .

• Dans RNI, on a directement accès à la représentation binaire de l'objet ce qui permet de manipuler directement les champs de celui-ci.

Le format binaire d'un objet n'est pas forcément le même d'une machine virtuelle à l'autre...

Recommended