interactions swing/web services java

footballwoman
interactions swing/web services java

bonjour,

J'ai réalisé un web service en java sous Eclipse WTP, en utilisant Tomcat. je dois maintenant intégrer le client dans le logiciel développé par mon équipe. Le logiciel peut etre lancé en ligne de commande ou bien via une interface graphique.

Je rencontre actuellement un probleme :

- lorsque je lance le logiciel en ligne de commande et que j'utilise la fonction d'appel au service web, tout fonctionne tres bien, la requete et la réponse au service web est très rapide.

- lorsque je lance le logiciel via l'interface graphique et que j'utilise cette meme fonction, la requete et la réponse au service web sont très lentes. Nous avons compris que l'interface graphique interagissait avec le client axis, et nous avons pensé créer un thread "à part" qui gere cet appel au service web. Cela ne change rien au probleme.
C'est bizarre car maintenant, comme ce sont deux threads différents, il ne devrait plus y avoir d'interactions... Je ne comprends pas.

Voici la fonction qui prend le plus de temps :
Code :

java.lang.Object _resp = _call.invoke(Object...)

dans la classe MonServiceSoapBindingStub : c'est une classe créée par éclipse lors de la génération du client, ainsi que la méthode.

Pourriez-vous m'aider? Avez-vous déjà rencontré ce genre de probleme? Avez-vous une solution?
Ai-je été claire?
Merci d'avance

fredericmazue

Oui tu as été claire, mais pas suffisament complète.

Quote:
Nous avons compris que l'interface graphique interagissait avec le client axis

D'abord, de quel axis parlons nous ? org.apache.axis ?
Ensuite qu'avez vous donc compris exactement ? On dirait que l'interaction se fait à l'insu de votre plein gré ce qui est assez bizarre.
Quote:
et nous avons pensé créer un thread "à part" qui gere cet appel au service web

A priori l'idée est bonne mais ->
Quote:
, comme ce sont deux threads différents, il ne devrait plus y avoir d'interactions... Je ne comprends pas.

-> Ben peut être que si. Si le résultat de l'appel au Web Service doit apparaître dans l'interface utilisateur, faut bien qu'il y ait interaction. Et il faut que celle-ci soit bien codée.
Tu dis que le Web Service devient lent. Mais es tu sûre d'abord. Est-ce bien qu'il est lent ou que les résultats tardent à être affiché dans l'interface ? C'est que ce n'est pas la même chose.
Quote:
Voici la fonction qui prend le plus de temps :
Code :

java.lang.Object _resp = _call.invoke(Object...)


Pardonne moi, mais ça c'est le genre d'info qui ne sert à rien. J'ai justement eu un échange un peu houleux avec quelqu'un sur ce forum très récement, pour une question très semblable d'ailleurs :)
Bref voilà. Ca ne sert à rien de donner le nom d'une méthode qui est lente lorsqu'on l'appelle et de nous dire qu'elle est lente quand on l'appelle. Que veux tu que l'on puisse en déduire ? Rien.
Il faut au moins montrer le code DANS la méthode.

Cela dit, je trouve bizarre la présence de invoke ici. Passer par la réfléxion de Java pour appeler un Web Service... Est-ce bien certain que c'est le moyen le plus simple et le plus rationnel ?
Du coup voir un peu de code en amont de cet appel pourrait bien être intéressant aussi. Et il faudrait que tu détailles ce que tu appelles "interaction".
Moyennant quoi, j'aurais peut être une solution à te proposer. Enfin je l'espère :)

footballwoman

Bonjour, voici qq compléments d'infos :

Quote:
D'abord, de quel axis parlons nous ? org.apache.axis ?

oui, oui

Quote:
Si le résultat de l'appel au Web Service doit apparaître dans l'interface utilisateur, faut bien qu'il y ait interaction. Et il faut que celle-ci soit bien codée.
Tu dis que le Web Service devient lent. Mais es tu sûre d'abord. Est-ce bien qu'il est lent ou que les résultats tardent à être affiché dans l'interface ? C'est que ce n'est pas la même chose.

non, non, c'est bien l'appel au web service qui est lent, pas de probleme pour l'affichage des résultats

Quote:
Cela dit, je trouve bizarre la présence de invoke ici. Passer par la réfléxion de Java pour appeler un Web Service... Est-ce bien certain que c'est le moyen le plus simple et le plus rationnel ?
Du coup voir un peu de code en amont de cet appel pourrait bien être intéressant aussi. Et il faudrait que tu détailles ce que tu appelles "interaction".

Voici un bout de code plus complet qui je l'espere nous aidera :

voila en quoi consiste l'utilisation du service web :

public TransitionGraph callWS(){
		CADP_WSService service = new CADP_WSServiceLocator(); //localisation du service web
		CADP_WS port;	
		try {
			port = service.getCADP_WS(); //connexion au service web
			String AUTFile= port.step1(); //premiere etape
			port.initializeAUTFile(AUTFile, numberOfTransitions, numberOfStates);//deuxieme etape
			GraphElement[] tab = new GraphElement[GraphElement.size()];
			for (int k=0;k<listGraphElement.size();k++){
				tab[k]=listGraphElement.get(k);
			}
			port.buildAUTFile(tab, AUTFile);//sending of the GraphElement table to the web service : construction of the .aut file on the server-side

			response=port.callCADP(AUTFile); //récuperation de la réponse du service web
			tabRetour = port.getGraph(AUTFile);//recuperation du graphe renvoyé par le service web
			//construit le graph partiel à retourner :
			ArrayList<QState> selectedStates = new ArrayList<QState>();
			for (int k=1;k<tabRetour.length;k++){			
				QState etatAAjouter = this.getState(tabRetour[k]);
				selectedStates.add(etatAAjouter);
			}
			PartialTransitionGraph partialGraph = new PartialTransitionGraph(this,selectedStates);
			graphRetour=partialGraph;
		} catch (IOException e) {
			e.printStackTrace();
		}
		catch (ServiceException e1) {
			e1.printStackTrace();  
		}
return graphRetour
}

Si on n'utilise pas l'interface graphique, cette fonction est appelée directement et prend 8s (pour envoyer et attendre la réponse pour un graphe de 2000 transitions).
si on utilise l'interface, elle est appelée via : (cela prend 81 s pour le mm graphe !)

public void appelleWS{
worker = new ThreadWorker() { //nouveau thread
			public Object construct() {
					resultingGraph = transitionGraph.exportToCADP();//appel et execution du service web
				return "";//pour la methode construct
			}
			public void finished() {
				exportButton.setEnabled(true);
				abortButton.setEnabled(false);
				if ( txtStatus.getText().equals(RUNNING) ) {
					txtStatus.setText(FINISHED);
					setResultsPanel();
				}
			}
		};
		worker.start();

	}

Je voulais préciser que les classes CADP_WSService, CADP_WSServiceLocator et CADP_WS ont été générées par Eclipse lorsque j'ai demandé de générer un client, je les ai explorées (c'est compliqué d'ailleurs) et c'est elles qui font appel à _call.invoke.

Si la génération du client par Eclipse n'est pas la bonne méthode à utiliser, je veux bien un peu d'aide pour appeler un web service, sans passer par cette création de classes compliquées d'Eclipse (serviceLocator...).

Ai-je tout dit ou manque-t-il des choses?
Merci !

[/]
fredericmazue

Quote:
Si on n'utilise pas l'interface graphique, cette fonction est appelée directement et prend 8s (pour envoyer et attendre la réponse pour un graphe de 2000 transitions).
si on utilise l'interface, elle est appelée via : (cela prend 81 s pour le mm graphe !)

Mais non, ça n'a pas de sens. le WS est ce qu'il est et quand tu l'appelles, il fait ce qu'il a à faire, toujours de manière identique.
AMHA le problème N'EST PAS dans le WS ! Mais, encore une fois, dans la manière de l'appeler ou de récupérer et afficher les résultats de son travail.

Le pb est, il me semble, dans le deuxième bout de code que tu donnes (et éventuellement dans des bouts semblables que tu ne montres pas). D'abord je voudrais savoir de quelle classe ThreadWorker il s'agit. Il y en a des floppées de ThreadWorker dans le monde de Java.
Le pb potentie se situe, je pense, dans ces apparemment imnocentes petites lignes.

Quote:

exportButton.setEnabled(true);

Si tu es dans un thread, tu ne peux normalement pas faire ça, c'est à dire APS appeler une méthode Swing qui change l'état d'un composant graphique. Swing ne fontionne correctement, (si jamais il fonctionne correctement.... mais c'est un autre débat :) ) QUE si ses méthodes sont invokées dans son propre thread, c'est à dire dans le thread principal de l'application. Sinon, dans un thread séparé, tu dois invoquer les méthodes telles que setEnabled par le biais de java.awt.EventQueue.invokeLater.

Ensuite une remarque. Tu as dis dans ton premier post que thread ou pas ça ne changeait rien. En apparence, car vu le me..... généré par Eclipse, je subodore (conforté en cela par la présence de invoke (qui n'a rien à voir avec invokeLoater)) que l'appel au WS est toujours dans un thread automatiquement lancé par la moulinette générée par Eclipse, donc tu as toujours le pb. Sauf bien sûr sans interface graphique du tout, pusique ce sont les appels aux méthodes de Swing qui créent le dysfonctionnement.

Quote:
Je voulais préciser que les classes CADP_WSService, CADP_WSServiceLocator et CADP_WS ont été générées par Eclipse lorsque j'ai demandé de générer un client, je les ai explorées (c'est compliqué d'ailleurs) et c'est elles qui font appel à _call.invoke.

Encore une atrocité comme seul le monde de Java sait en produire :( :cry:

Quote:
Si la génération du client par Eclipse n'est pas la bonne méthode à utiliser, je veux bien un peu d'aide pour appeler un web service, sans passer par cette création de classes compliquées d'Eclipse (serviceLocator...).

Si tu as ton Programmez! 74 sous la main, il y a un d'article d'initiation aux Web Service avec Java.

J'ai déjà rencontré le genre de problème que tu décris et qui est assez courant d'ailleurs :(
En principe je t'ai donné, sinon la solution, la bonne piste :)
Enfin je l'espère.

fredericmazue

Ah :!:
Je crois que je viens de comprendre :!: :)

Cette méthode là

Quote:
public void finished() { 
            exportButton.setEnabled(true); 
            abortButton.setEnabled(false); 
            if ( txtStatus.getText().equals(RUNNING) ) { 
               txtStatus.setText(FINISHED); 
               setResultsPanel(); 
            } 
         } 

Ca ressemble à une méthode de finalisation.
C'est à dire une méthode appellée quand la classe est garbage-collectée par la JVM ou finalisée par le framework. Enfin bref, appellée de temps en temps à des moments imprévisibles et surtout se faisant désirer.

Essaie en mettant le code de cette méthode à la fin de la méthode construct dans ton code donné plus haut. Je veux dire juste avant le return"";

ATTENTION :!: :!: :!:
J'insiste très lourdement tu vas devoir aussi y ajouter *obligatoirement* les java.awt.EvenQueue.invokeLater que j'ai évoqués plus haut. Moyennant quoi ton problème sera réglé :)

footballwoman

voila qq précsions par rapport à ton premier post :

la classe ThraedWorker :

import javax.swing.SwingUtilities;

public abstract class ThreadWorker {
    private Object value;  // see getValue(), setValue()
    private Thread thread;

    /**
     * Class to maintain reference to current worker thread
     * under separate synchronization control.
     */
    private static class ThreadVar {
        private Thread thread;
        ThreadVar(Thread t) { thread = t; }
        synchronized Thread get() { return thread; }
        synchronized void clear() { thread = null; }
    }

    private ThreadVar threadVar;

    /**
     * Get the value produced by the worker thread, or null if it
     * hasn't been constructed yet.
     */
    protected synchronized Object getValue() {
        return value;
    }

    /**
     * Set the value produced by worker thread
     */
    private synchronized void setValue(Object x) {
        value = x;
    }

    /**
     * Compute the value to be returned by the <code>get</code> method.
     */
    public abstract Object construct();

    /**
     * Called on the event dispatching thread (not on the worker thread)
     * after the <code>construct</code> method has returned.
     */
    public void finished() {
    }

    /**
     * A new method that interrupts the worker thread.  Call this method
     * to force the worker to stop what it's doing.
     */
    public void interrupt() {
        Thread t = threadVar.get();
        if (t != null) {
            t.interrupt();
        }
        threadVar.clear();
    }

    /**
     * Return the value created by the <code>construct</code> method.
     * Returns null if either the constructing thread or the current
     * thread was interrupted before a value was produced.
     *
     * @return the value created by the <code>construct</code> method
     */
    public Object get() {
        while (true) {
            Thread t = threadVar.get();
            if (t == null) {
                return getValue();
            }
            try {
                t.join();
            }
            catch (InterruptedException e) {
                Thread.currentThread().interrupt(); // propagate
                return null;
            }
        }
    }


    /**
     * Start a thread that will call the <code>construct</code> method
     * and then exit.
     */
    public ThreadWorker() {
        final Runnable doFinished = new Runnable() {
           public void run() { finished(); }
        };

        Runnable doConstruct = new Runnable() {
            public void run() {
                try {
                    setValue(construct());
                }
                finally {
                    threadVar.clear();
                }

                SwingUtilities.invokeLater(doFinished);
            }
        };

        Thread t = new Thread(doConstruct);
        threadVar = new ThreadVar(t);
    }

    /**
     * Start the worker thread.
     */
    public void start() {
        Thread t = threadVar.get();
        if (t != null) {
            t.start();
        }
    }
}

donc la méthode finished() est bien appelée apres la methode run, non?

Quote:

Si tu es dans un thread, tu ne peux normalement pas faire ça, c'est à dire APS appeler une méthode Swing qui change l'état d'un composant graphique.

ah, ok, je savais pas...

Quote:
Swing ne fontionne correctement, (si jamais il fonctionne correctement.... mais c'est un autre débat Smile ) QUE si ses méthodes sont invokées dans son propre thread, c'est à dire dans le thread principal de l'application. Sinon, dans un thread séparé, tu dois invoquer les méthodes telles que setEnabled par le biais de java.awt.EventQueue.invokeLater.

il faut que je fasse quelque chose du genre
java.awt.EventQueue.invokeLater(new Runnable){
run(){
setEnabled(true)....
}}

Il faut aussi que j'enleve threadworker .... et que je fasse plutot :

public void appelleWS{
      resultingGraph = transitionGraph.exportToCADP();//appel et execution du service web
      java.awt.EventQueue.invokeLater(new Runnable){
           run(){
                 exportButton.setEnabled(true);
                 abortButton.setEnabled(false);
                 if ( txtStatus.getText().equals(RUNNING) ) {
                      txtStatus.setText(FINISHED);
                      setResultsPanel();
                 }
           }
        };
   } 

Est-ce que c'est ca que je dois faire?
et la, java sais qu'il faut d'abord faire "resultingGraph=..." avant de mettre à jour les boutons et tout ca?

Quote:
Si tu as ton Programmez! 74 sous la main, il y a un d'article d'initiation aux Web Service avec Java.

malheureusement, je ne l'ai pas, je me suis abonnée a partir du 80!

Merci

fredericmazue

Quote:
donc la méthode finished() est bien appelée apres la methode run, non?

Certainement, puisque la doc le dis, mais la question c'est *quand*. Faudrait pas que ça soit dans 8 s.... si tu vois ce que je veux dire.

Quote:
ah, ok, je savais pas...

Mais si, je te l'ai dit dans mon tout premier post :)
Quote:
il faut que je fasse quelque chose du genre

Oui, avec

exportButton.setEnabled(true);

Ca sera encore mieux.

Quote:
Il faut aussi que j'enleve threadworker .... et que je fasse plutot :

OUI! Exactement! :)

Quote:
et la, java sais qu'il faut d'abord faire "resultingGraph=..." avant de mettre à jour les boutons et tout ca?

Le jour où Java saura quelque chose d'intelligent, les poules auront des crocs. Mais ton code et ta compréhension sont clairs et Java FERA comme tu dis, d'abord, resultGraph=... puis rafraîchissement des composants Swing :)

Quote:
malheureusement, je ne l'ai pas, je me suis abonnée a partir du 80!

Bon tu es abonnée, donc pardonnée :)

Et vu ce que tu m'as dit plus haut, tu n'as pas besoin de l'article. Tu vas savoir te débrouiller avec le framework généré par Eclipse, j'en suis sûr. :)

footballwoman

snif...
je suis triste, :cry:
ca ne marche toujours pas mieux...
j'ai fais comme tu m'avais dit avec java.awt.EventQueue ... mais pas d'améliorations...
Et ce que ca peut venir du fait que je passe à mon web service un tableau de 2000 éléments?
Pourtant, ce devrait aussi poser probleme sans l'utilisation de l'interface...
Je ne sais vraiment plus quoi faire, je suis déprimée
As-tu une solution de recours?
Merci

fredericmazue

Quote:
Et ce que ca peut venir du fait que je passe à mon web service un tableau de 2000 éléments?
Pourtant, ce devrait aussi poser probleme sans l'utilisation de l'interface...

En effet, si le WS marche bien sans l'interface graphique, il doit marcher aussi bien avec, une fois pour toutes.

Quote:
Je ne sais vraiment plus quoi faire, je suis déprimée
As-tu une solution de recours?

Tu as fait la correction comme que je t'ai expliqué à partir de ce que tu m'as montré comme code. Il y a peut être (probablement!) d'autres endroits à corriger. On a vu pour activer un bouton, mais je suppose qu'il y a plus dans cette interface graphique. Ca il n'y a que toi qui peut le voir, mais je pense que je t'ai mise sur la piste.
N'hésite pas à venir poser de nouvelles questions si ça ne suffit pas.

footballwoman

youpi!
je viens de comprendre.
en fait, c'est du à une config du programme : axis surveille swing avec log4j.LF5.LF5Appelnder, donc rien à voir avec les rafraichissement de swing, ni mon service web... donc, ya just une config à changer!
je suis contente d'avoir trouvé, mais j'y aurai quand mm perdu du temps...
Encore merci fred!
A bientot

fredericmazue

Quote:
en fait, c'est du à une config du programme : axis surveille swing avec log4j.LF5.LF5Appelnder, donc rien à voir avec les rafraichissement de swing

Ben si ça a à voir, tu viens de le dire toi même :)
Ca n'a pas à voir avec ton code proprement dit, ok, et c'est bien pour ça que je t'ai dit de chercher autour si la piste de Swing.

Par contre je ne savais pas pour la config et je suis herureux d'avoir appris quelque chose grâce à toi. :)

Quote:
je suis contente d'avoir trouvé

Je le crois volontiers :)
Quote:
mais j'y aurai quand mm perdu du temps

Avec Java, tu n'as pas fini d'en voir des comme ça...

Au plaisir de te revoir sur ce forum :)