Interaction entre desktop et web

Dans une application Eclipse RCP le composant Browser permet de mixer web et desktop. Les avantages de cette technique sont nombreux, notamment quand il s’agit de réutiliser un existant Web pour l’encapsuler dans une application desktop. L’interêt est de ne plus être dépendant d’un navigateur mais d’être son propre navigateur. Cependant pour que cela fonctonne il faut faire communiquer Web et desktop. 6 techniques proposées pour y parvenir…

Dans une application Eclipse RCP le composant Browser permet de mixer web et desktop. Les avantages de cette technique sont nombreux, notamment quand il s’agit de réutiliser un existant Web pour l’encapsuler dans une application desktop. L’interêt est de ne plus être dépendant d’un navigateur mais d’être son propre navigateur. Cependant pour que cela fonctonne il faut faire communiquer web et desktop. Lorsqu’il s’agit d’envoyer des données à l’application Web cela reste simple en modifiant l’URL (setURL()) ou en executant du Javascript (execute()). Mais lorsqu’il s’agit de récupérer des données depuis le web c’est plus fastidieux. Je vous propose ici 6 techniques :
1. via la barre de statut: c’est le code que l’on trouve sur les snippets SWT (Snippet160)
2. via la barre d’adresse: comme le propose Peter Nehrer
3. en requetant directement le DOM : depuis la 3.3 il est possible d’utiliser Mozilla et l’API XPCOM fournie la possibilité d’accèder au DOM de la page HTML. Ci-dessous un petit exemple reprenant le Snippet267 mais en requetant en XPath pour récupérer le titre d’un champ. Les possibilités sont multiples à l’image de l’utilisation qu’en fait ATF (faudrait que j’y consacre un article complet pour explorer toutes les possibilités qu’offre XPCOM).

static Browser browser;
public static void main(String[] args) {
    Display display = new Display();
    Shell shell = new Shell(display);
    shell.setLayout(new GridLayout(2, true));
    shell.setText("Use Mozilla's Design Mode");
    try {
        browser = new Browser(shell, SWT.MOZILLA);
    } catch (SWTError e) {
        System.out.println("Could not instantiate Browser: " + e.getMessage());
        return;
    }
    browser.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, true, 2, 1));
    Button searchButton = new Button(shell, SWT.PUSH);
    searchButton.setText("Search");
    searchButton.addListener(SWT.Selection, new Listener() {
        public void handleEvent(Event event) {
            search();
        }
    });
    browser.setUrl("http://www.google.com");
    shell.setSize(400, 400);
    shell.open();
    while (!shell.isDisposed()) {
        if (!display.readAndDispatch())
             display.sleep();
    }
    display.dispose();
}

public static boolean search() {
    nsIWebBrowser webBrowser = (nsIWebBrowser) browser.getWebBrowser();
    if (webBrowser == null) {
        System.out.println("Could not get the nsIWebBrowser from the Browser widget");
        return false;
    }
    nsIDOMWindow window = webBrowser.getContentDOMWindow();
    nsIDOMDocument document = window.getDocument();
    nsIDOMXPathEvaluator xpath = (nsIDOMXPathEvaluator) document.queryInterface(nsIDOMXPathEvaluator.NS_IDOMXPATHEVALUATOR_IID);
    nsIDOMXPathNSResolver res = xpath.createNSResolver(document);
    nsISupports obj = xpath.evaluate("//input[@name='q']/@title",
                                                 document,
                                                 res,
                                                 nsIDOMXPathResult.STRING_TYPE,
                                                 null);
    nsIDOMXPathResult result = (nsIDOMXPathResult) obj.queryInterface(nsIDOMXPathResult.NS_IDOMXPATHRESULT_IID);
    System.out.println(result.getStringValue());
    return true;
}


4. via un socket : en executant une XMLHTTPRequest via la méthode execute() qui fait appel à un socket ouvert pour l’occasion et permet de récupérer un contenu.

private String getDataFromBrowser() {
	String javascript = "try {";
	javascript += "xhr_object = new ActiveXObject(\"Microsoft.XMLHTTP\"); ";
	javascript += "xhr_object.open(\"POST\", \"http://localhost:9091\", false);";
	javascript += "xhr_object.setRequestHeader(\"Content-type\", \"application/x-www-form-urlencoded\");";
	javascript += "xhr_param = generateXML();"; // appel d'une fonction retournant un contenu
	javascript += "xhr_object.send(xhr_param);";
	javascript += "} catch (ex) { alert(\"Failed to save document\"); }";

	String xml = null;
	XMLPicker e = new XMLPicker();
	Thread t = new Thread(e);
	t.start();

	// Attend que le thread démarre.
	try {
		Thread.sleep(10);
	} catch (InterruptedException eee) {
	}
	// Demande au browser un contenu.
	boolean ok = browser.execute(javascript);

	int wait = 0;
	while (xml == null && wait < 10)
		try {
			Thread.sleep(10);
			wait++;
			xml = e.getXML();
		} catch (InterruptedException eeee) {
		}
	System.out.println(xml);
	return xml;
}

private class XMLPicker implements Runnable {
	private String xml;
	public String getXML() {
		return xml;
	}

	public void run() {
		ServerSocket server = null;
		try {
			server = new ServerSocket(9091);
			server.setSoTimeout(5000);
			System.out.println("Waiting for response");
			Socket socket = server.accept();
			// Traitement du flux, la classe HTTPServer est disponible pour ceux que ca intéresse
			xml = HttpServer.getXMLString(socket, "UTF-8"); 
			socket.close();
		} catch (IOException ee) {
			System.err.println(ee);
		} finally {
			if (server != null) {
				try {
					server.close();
				} catch (Exception e) {
				// Ignore.
				}
			}
		}
	}
}

5. en démarrant un serveur http dans la meme JVM que Eclipse RCP : tomcat peut être embarqué et demarré depuis une application Java, c'est aussi le cas de WebObjects (Application.primeApplication()). Les objets étant issus de la même JVM leur statut est partagée par les deux application mais attention toutefois aux classloaders.
6. via terracota : ce cluster de JVM permet de synchroniser les objets de 2 JVM et de répercuter en temps réel les états des objets.

Une réflexion sur « Interaction entre desktop et web »

  1. Une variante de la solution 3 est donnée par Bryan Campbell dans Deep Integration of GWT/JavaScript and Eclipse using XULRunner and JavaXPCOM.
    La variante consiste à implémenter un composant XPCOM et non pas simplement à en utiliser un déjà existant (comme celui de l’accès au DOM).
    L’inconvénient est le caractère plus statique de la solution (compilation d’interfaces IDL).
    L’avantage est que c’est bien plus propre (moins bidouille) et rapide que de parser le DOM (d’un autre côté le parsing se fait en Java et une partie de l’application est en Javascript : le gain est donc probablement imperceptible).
    L’intégration d’une application GWT dans une application Eclipse RCP est du coup assez sympathique (quasi 100% Java).
    Je l’utilise d’ailleurs dans un projet professionnel pour que notre application GWT puisse tourner en tâche de fond dans la zone de notification et puisse informer l’utilisateur d’évènements liés à notre SI via des info bulles.
    J’ai songé à utiliser la solution 5 mais je crois que la Same Origin Policy des navigateurs aurait apporté son lot de difficulté (à cause du double accès local/intranet).
    Dans tous les cas, merci pour ton billet qui m’a facilité la prospection sur l’interaction entre desktop et web.

Les commentaires sont fermés.