jan 03 2011

[Post-it] Convertir un WSDL (RPC-Encoding) en fichier Java

Catégorie(s): DéveloppementSébastien @ 21:14

Pour interroger un webservice SOAP, il m’a été nécessaire de générer tout un ensemble de classes à l’aide d’un fichier WSDL (RPC). J’ai tout d’abord tenté d’utiliser Axis2 et sa classe WSDL2Java mais j’ai été confronté à plusieurs erreurs du type :

org.apache.axis2.schema.SchemaCompilationException:
can not find type {http://schemas.xmlsoap.org/soap/encoding/}
Array from the parent schema urn:Interface

La raison est simple : Axis2 n’accepte pas les WSDL encodés suivant le type RPC. RPC est en effet une vieille spécification de SOAP 1.1 qui rencontre de gros soucis de performances dès que la charge augmente (cf. Discover SOAP encoding’s impact on Web service performance).

Dans mon cas, je n’ai pas le choix et je dois obligatoirement d’utiliser ce fichier pour interroger un webservice tiers. Il m’a donc fallu utiliser Axis 1.4 et au final ça c’est avéré hyper simple vu que ça se résume à une ligne de commande :

java -classpath "lib\*" org.apache.axis.wsdl.WSDL2Java -av -p [PAKAGE NAME] [WSDL FILE] -D

Donc on commence par passer le dossier « lib » comme classpath. On appelle ensuite la classe WSDL2Java avec les arguments suivants :

  • -a = générer le code pour tous les éléments ;
  • -v = Afficher ce que WSDL2Java génère lorsqu’elle le fait ;
  • -p = Nom du package dans lequel seront contenu les classes générées ;
  • -D = Afficher les informations de débogage.

Le dernier n’est pas obligatoire mais j’en ai eu besoin parce que le fichier WSDL fournit n’était pas valide (2 erreurs que j’ai dû corriger manuellement).

Et voilà vos classes Java générées. C’était pas si compliqué que ça quand on oublie pas de faire attention à l’encodage du fichier WSDL. Seul bémol : le code généré n’est pas ce qu’il y a de plus propre et vous risquez de devoir mettre le nez dedans pour corriger certains trucs.

Sources et infos complémentaires :


oct 22 2010

[Post-it] Supprimer les fichiers de plus de X jours sous *nix

Catégorie(s): DéveloppementSébastien @ 19:34

Les fichiers de cache peuvent consommer beaucoup d’espace disque surtout lorsque le site a un traffic important. Comment faire pour supprimer les fichiers obsolètes sous *nix ? Avec une simple ligne de commande :

find -type f -mtime +2 -delete

Et hop, supprimés les fichiers qui n’ont pas été modifiés depuis plus de deux jours ! Reste plus qu’à planifier cette commande une fois par jour/semaine/etc via un CRON et le problème du dossier de cache qui bouffe tout l’espace disque est réglé. Pensez également à changer le 2 par la durée de conservation des fichiers (en jour).

Note : Il peut être possible de faire réaliser cette opération par le gestionnaire du cache en lui-même (genre avant de créer le cache, on supprime tout ce qui est périmé). Suivant comment c’est codé, ça risque de bouffer énormément de temps et donc ralentir le chargement des pages. Sur un site à petit traffic, ça peut ne pas posser de problème autrement oui.

PS : Si vous avez d’autres idées liées à la gestion des fichiers de cache, hésitez pas à m’en faire part dans les commentaires. :)


juil 22 2010

[Java] HttpClient 4 et authentification HTTP

Catégorie(s): DéveloppementSébastien @ 22:24

Tavailler avec différentes API permet souvent de faire des découvertes assez intéressantes. Celle d’aujourd’hui concerne l’authentification HTTP en utilisant la librairie HttpClient v4.0.1.

Pour appeler un webservice qui génère un rapport, on m’a donné une simple url :

http://user:pass@domain.com/get_report

Le nom d’utilisateur et le mot de passe comme ça, j’avoue que c’est pas ce que j’aurai fait mais étant donné que c’est pas un webservice interne, j’ai pas le choix. Un petit bout de code accompagnait cette url :

<?php
echo file_get_contents('http://user:pass@domain.com/get_report');
?>

Je teste et ça fonctionne correctement. Etant donné que mon outil est en Java et vu la facilité du truc, la mirgration depuis PHP ne risque, à première vue, pas poser trop trop de problèmes vu que j’ai déjà pas mal bossé avec la librairie HttpClient. Je m’attèle donc à la création d’une classe toute bête pour tester rapidement :

package net.delistage.httpauth;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
public class HttpAuthTest1 {
	private static final String URL = "http://user:pass@domain.com/get_report";
	/**
	 * We simply get the report from URL.
	 *
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			DefaultHttpClient client = new DefaultHttpClient();
			// Our request method
			HttpGet httpGet = new HttpGet(HttpAuthTest1.URL);
			// Try to get the report
			HttpResponse response = client.execute(httpGet);
			HttpEntity entity = response.getEntity();
			String content = IOUtils.toString(entity.getContent());
			System.out.println(content);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

Comme vous pouvez le constater, rien de bien compliqué ou exotique : c’est basique à souhait. Et là bien sûr… ça ne fonctionne pas. Le contenu récupéré n’est pas le rapport mais le formulaire de login au webservice (parce que oui, il dispose d’un accès web en plus de l’API). Je ne me décourage pas et je tente d’envoyer le nom d’utilisateur et de mot de passe plus proprement en utilisant le CredentialsProvider :

package net.delistage.httpauth;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
public class HttpAuthTest2 {
	private static final String URL = "http://domain.com/get_report";
	private static final String USER = "user";
	private static final String PASS = "pass";
	/**
	 * We simply get the report from URL after being authenticated using simple
	 * credentials.
	 *
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			DefaultHttpClient client = new DefaultHttpClient();
			// Our request method
			HttpGet httpGet = new HttpGet(HttpAuthTest2.URL);
			// Set credentials
			UsernamePasswordCredentials creds = new UsernamePasswordCredentials(
					HttpAuthTest2.USER, HttpAuthTest2.PASS);
			client.getCredentialsProvider()
					.setCredentials(AuthScope.ANY, creds);
			// Try to get the report again
			HttpResponse response = client.execute(httpGet);
			HttpEntity entity = response.getEntity();
			String content = IOUtils.toString(entity.getContent());
			System.out.println(content);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

C’est déjà mieux en théorie. Je re-tente et là bingo : ça ne fonctionne toujours pas. Ca serait trop simple non ? Bon réfléchissons à la cause du problème : on dirait que le serveur tente de faire la requête sur la page avant de se connecter avec les identifiants que je lui fourni. Direction la documentation de la librairie concernant l’authentication HTTP et plus particulièrement le paragraphe sur l’authentification préventive. Pour cela, il faut utiliser une classe implémentant l’interface HttpRequestInterceptor. Le code d’exemple est assez clair mais plutôt que de l’implémenter dans une méthode, je préfère le faire dans une classe dédiée :

package net.delistage.httpauth;
import java.io.IOException;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthState;
import org.apache.http.auth.Credentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpContext;
public class BasicAuthRequestInterceptor implements HttpRequestInterceptor {
	public void process(final HttpRequest request, final HttpContext context)
			throws HttpException, IOException {
		AuthState authState = (AuthState) context
				.getAttribute(ClientContext.TARGET_AUTH_STATE);
		CredentialsProvider credsProvider = (CredentialsProvider) context
				.getAttribute(ClientContext.CREDS_PROVIDER);
		HttpHost targetHost = (HttpHost) context
				.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
		// If not auth scheme has been initialized yet
		if (authState.getAuthScheme() == null) {
			AuthScope authScope = new AuthScope(targetHost.getHostName(),
					targetHost.getPort());
			// Obtain credentials matching the target host
			Credentials creds = credsProvider.getCredentials(authScope);
			// If found, generate BasicScheme preemptively
			if (creds != null) {
				authState.setAuthScheme(new BasicScheme());
				authState.setCredentials(creds);
			}
		}
	}
}

Il reste maintenant à l’utiliser dans ma classe et voir comment ça se comporte :

package net.delistage.httpauth;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
public class HttpAuthFinal {
	private static final String URL = "http://domain.com/get_report";
	private static final String USER = "user";
	private static final String PASS = "pass";
	/**
	 * We simply get the report from URL after being authenticated.
	 *
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			DefaultHttpClient client = new DefaultHttpClient();
			// Simple interceptor set as first interceptor in the protocol chain
			HttpRequestInterceptor preemptiveAuth = new BasicAuthRequestInterceptor();
			client.addRequestInterceptor(preemptiveAuth, 0);
			// Our request method
			HttpGet httpGet = new HttpGet(HttpAuthFinal.URL);
			// Set credentials
			UsernamePasswordCredentials creds = new UsernamePasswordCredentials(
					HttpAuthFinal.USER, HttpAuthFinal.PASS);
			client.getCredentialsProvider()
					.setCredentials(AuthScope.ANY, creds);
			// We can get the report now !
			HttpResponse response = client.execute(httpGet);
			HttpEntity entity = response.getEntity();
			String content = IOUtils.toString(entity.getContent());
			System.out.println(content);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

Et cette fois, le test est concluant : mon rapport s’affiche bien correctement ! Par contre il reste un problème annoncé par la documentation : utiliser un intercepteur est un risque de sécurité car il est susceptible d’envoyer en clair les identifiants de connexion à des tiers non authorisés. Dans la mesure où je n’ai pas encore trouvé mieux ou une autre solution pour que ça fonctionne, je vais devoir faire avec malgré cela. Si des lecteurs passent par là et ont une autre solution ou idée, je suis tout ouïe.


juin 25 2010

Java 4-ever

Catégorie(s): On the netSébastien @ 20:48

Je sors de mon long silence radio, pour vous partager cette petite vidéo qui risque malheureusement de ne faire sourire que les développeurs.

Source : Code 18.

Et maintenant je retourne dans ma grotte !


avr 05 2010

CHRYSEIS – Planet Dead released

Catégorie(s): C.H.R.Y.S.E.I.S.,MusiqueSébastien @ 17:32

Planet Dead released Ca y est le précieux est disponible ! Je parle bien sûr du LP Planet Dead de CHRYSEIS qui vient tout juste de sortir (Great Dane Records / Season of Mist).

Il est disponible en trois packs :

Pack 1 : CD « Planet Dead » + Stickers (13€)
Pack 2 : CD « Planet Dead » + tshirt (L/XL/XXL) + Stickers (20€)
Pack 3 : CD « Planet Dead » + CD « Presence of the Past » + tshirt (L/XL/XXL) + Stickers (23€)

Pour le commander, vous trouverez plus d’infos sur MySpace : « PLANET DEAD » RELEASED!!!.

Pour ceux que ça intéresse également, sachez que vous pouvez trouver dans le Metallian de ce trimestre une interview et une chronique de l’album.


« Page précédentePage suivante »