Posts Tagged ‘Java’

WikiDump: Tool to get Wikipedia content

Wednesday, June 30th, 2010

WikiDump tool has been made to get Wikipedia content from a list of article ids. The tool connect on http://en.wikipedia.org to get text data.

For more informations about getting Wikipedia content, you can see this post.

Here is the source code:


package com.devbypractice;

import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

public class WikiDump
{
 /**
 * Get xhmlt content of Wikipedia source to String
 *
 * @param sUrlSource Url Source
 * @param sEncoding Encoding
 * @return xhtml content
 * @throws Exception
 */
 public static String dumpWikiXhtmlToString(String sUrlSource, String sEncoding)
 throws Exception
 {
 String inputLine;

 URL url;
 url = new URL(sUrlSource);
 URLConnection urlConn = url.openConnection();

 // TimeOut 30sec
 //
 urlConn.setConnectTimeout(30000);
 BufferedReader in = new BufferedReader(new InputStreamReader(
 urlConn.getInputStream(), sEncoding));

 StringBuilder sb = new StringBuilder();

 // XML header
 //
 sb.append("<?xml version=\"1.0\"?>");
 sb.append("<WikiContent>");

 // Copy response to buffer
 //
 while ((inputLine = in.readLine()) != null) {
 sb.append(inputLine);
 sb.append("\n");
 }
 in.close();

 // XML footer
 //
 sb.append("</WikiContent>");

 return sb.toString();
 }

 /**
 * Load article ids from files File must have an id on each line.
 *
 * @param sFileName
 * @return Articles ids
 * @throws Exception
 */
 public static List<Integer> loadArticleIdsFromFile(String sFileName)
 throws Exception
 {
 List<Integer> articleIds = new ArrayList<Integer>();

 FileReader reader = new FileReader(sFileName);
 BufferedReader buffReader = new BufferedReader(reader);

 String sLine = "";

 while ((sLine = buffReader.readLine()) != null) {
 Integer id = Integer.parseInt(sLine);
 articleIds.add(id);
 }

 buffReader.close();

 return articleIds;

 }

 /**
 * Display usage
 */
 public static void displayUsage()
 {
 System.out.println("Arguments:");
 System.out.println("<Article id source file path> <Output directory>");
 System.out.println("");
 System.out.println("Notes:");
 System.out.println("Article id file must have an id on each line. ");
 System.out.println("Output directory must exist.");

 }

 /**
 * Text cleanup. References [12], [3] etc... are deleted.
 *
 * @param text Text to clean
 * @return Cleaned text
 */
 public static String cleanup(String text)
 {
 return text.replaceAll("\\[[0-9]+\\]", "");
 }

 /**
 * Main entry
 *
 * @param args Arguments
 */
 public static void main(String[] args)
 {
 try {

 // UTF-16 chars count for output files
 // 1000000 ~= 2 Mo
 //
 final int fileSizeLimit = 1000000;

 // Current output file size
 // set to MAX + 1 for the file to be created at first time
 //
 int currentFileSize = fileSizeLimit + 1;

 // Url to get online Wikipedia article by id
 //
 String sWikiUrlFormat = "http://en.wikipedia.org/w/index.php?action=render&curid=%d";

 FileOutputStream outStream = null;
 OutputStreamWriter writer = null;

 if (args.length != 2) {
 displayUsage();

 }
 else {

 // Get article ids from file
 //
 List<Integer> articleIds = loadArticleIdsFromFile(args[0]);

 // Output dir
 //
 String sDestinationDir = args[1];

 // For each article id
 //
 for (Integer articleId : articleIds) {

 try {

 if (currentFileSize > fileSizeLimit) {
 if (writer != null) {
 writer.close();
 }
 outStream = new FileOutputStream(sDestinationDir + "\\"
 + articleId + ".txt");
 writer = new OutputStreamWriter(outStream, "UTF-16");

 currentFileSize = 0;

 }

 // Build article url
 //
 String sWikiUrl = String.format(sWikiUrlFormat, articleId);

 // Get article content
 //
 String sWiki = dumpWikiXhtmlToString(sWikiUrl, "UTF-8");

 // Load Xhtml content
 //
 InputSource is = new InputSource(new StringReader(sWiki));
 Document doc = DocumentBuilderFactory.newInstance()
 .newDocumentBuilder()
 .parse(is);

 // writer.write("========= " + articleId + " ==========\n");

 // Target desired wikipedia content
 //
 NodeList nl = XPathProcessor.getInstance().EvaluateMutli(
 "/WikiContent/p", doc);

 // For each node
 //
 for (int i = 0; i < nl.getLength(); i++) {
 String sContent = "";
 try {
 Node node = nl.item(i);

 // get text content
 //
 sContent = node.getTextContent();

 // clean text
 //
 sContent = cleanup(sContent);

 // Write in output file
 //
 writer.write(sContent);
 writer.write("\n\n");
 writer.flush();

 // Increase output file size count
 //
 currentFileSize += sContent.length() + 2;
 }
 catch (Exception e2) {
 String sLog = "Paragraph not get " + i
 + " for id=" + articleId;
 System.out.println(sLog);
 }
 }
 }
 catch (Exception e3) {
 String sLog = "Article not get for id = " + articleId
 + "\n";
 System.out.println(sLog);

 e3.printStackTrace();
 }

 }
 writer.close();
 }
 }

 catch (Exception e) {
 e.printStackTrace();

 }

 }

}

To compile the source you will need a XPath query manager available on this link.

A compiled version of WikiDump can be downloaded here.

Get Wikipedia content

Tuesday, June 29th, 2010

Wikipedia is a huge source of content for people who need big text data.

Wikipedia allow to get these data and provide database and documentation on this link.

My own experience allow to make such statements:

  • It’s painfull to parse Wiki language (Wikecode or WikiText), even if many tools exist (Mylyn etc..), proprietary templates make substitutions not possible.
  • The best Wikitext parser is Wikipedia itself…
  • Creating a local Wikipedia mirror is painfull and poorly documented.
  • Get parsed arcticles from Wikipedia website is not a problem when it is as slow as 1 article per second, but getting a large amout of articles is very long (1 article = 1 sec <=> 100000 articles = 1 whole day)
  • Arcticles must be filtered because everything is in Wikipedia, best and worst.

I was able to get data from Wikipedia in these few steps:

  1. Prerequisites
    MySQL server and tools
    Java virtual machine (the path of java.exe must be in PATH environnement variable)
    Custom Java tool WikiDump [ Sources - Compiled version ] (in the compiled version a batch is provided to launch a demo,  an exception is thrown as expected but it doesn’t interrupt  the process )
  2. Go to website http://download.wikimedia.org/enwiki/latest/ to get MySQL dumps:
    enwiki-latest-category.sql.gz
    : Contains a list of categories and arcticles count.
    enwiki-latest-categorylinks.sql.gz
    : Makes the link between articles and categories.
  3. Import theses scripts in a MySQL database (with Windows I used MySQL Administrator).
    Note: The process is long (many hours).
  4. Filter wanted articles by executing the following query to get desired portals:SELECT * FROM category WHERE cat_pages > 0 AND cat_title LIKE “Portal:%” ORDER BY cat_pages DESCOnce portals choosen  (take and refuse), select included articles. Execute following query (put your own portals here):

    SELECT cl_data_in.cl_from FROM ((SELECT DISTINCT cl3.cl_from FROM categorylinks AS cl3 WHERE cl3.cl_to IN (“Portal:take1,Portal:take2″)) AS cl_data_in)
    WHERE cl_data_in.cl_from NOT IN (SELECT DISTINCT cl2.cl_from FROM categorylinks AS cl2 WHERE LOWER(cl2.cl_to LIKE “liste d%”) OR cl2.cl_to IN (“Disambiguation_pages”,”Portal:refuse1″))

    Once article list is filtered, export result in csv file, not matter file format, the goal is to have an article id by line.

    Cleanup the file, delete column name and empty lines.

  5. To launch process to get content, type in command line:
    java -jar WikiDump.jar article_file_path output_directory > log.txt

WikiDump : Outil de récupération du contenu de Wikipedia

Sunday, June 27th, 2010

L’utilitaire WikiDump permet de récupérer le contenu de Wikipedia à partir d’une liste d’identifiant d’articles. L’outil se connecte sur le site fr.wikipedia.org afin de récupérer des données à jour et correctement formattées.

Pour plus de détail concernant la récupération des données de Wikipedia rendez vous sur cet article.

Pour la curiosité ou bien pour modifier son comportement voici le code source:


package com.devbypractice;

import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

/**
 * Classe contenant un main permettant de faire un dump
 * de wikipédia.
 *
 */
public class WikiDump
{
 /**
 * Récupère le contenu xhtml d'une url source Wikipédia en String
 *
 * @param sUrlSource Url Source
 * @param sEncoding Encodage
 * @return Le contenu du html
 * @throws Exception
 */
 public static String dumpWikiXhtmlToString(String sUrlSource, String sEncoding) throws Exception {
 String inputLine;

 URL url;
 url = new URL(sUrlSource);
 URLConnection urlConn = url.openConnection();

 // TimeOut de 30sec
 //
 urlConn.setConnectTimeout(30000);
 BufferedReader in = new BufferedReader(
 new InputStreamReader(
 urlConn.getInputStream(), sEncoding));

 StringBuilder sb = new StringBuilder();

 // En tête XML
 //
 sb.append("<?xml version=\"1.0\"?>");
 sb.append("<WikiContent>");

 // Copie du contenu html vers le buffer
 //
 while ((inputLine = in.readLine()) != null){
 sb.append(inputLine);
 sb.append("\n");
 }
 in.close();

 // Fin XML
 //
 sb.append("</WikiContent>");

 return sb.toString();
 }

 /**
 * Charge la liste des identifiants des articles d'un fichier
 * vers un liste d'entiers.
 * Le fichier doit contenir un id sur chaque ligne.
 *
 * @param sFileName
 * @return Liste des identifiants des articles
 * @throws Exception
 */
 public static List<Integer> loadArticleIdsFromFile(String sFileName) throws Exception
 {
 List<Integer> articleIds = new ArrayList<Integer>();

 FileReader reader = new FileReader(sFileName);
 BufferedReader buffReader = new BufferedReader(reader);

 String sLine ="";

 while ((sLine = buffReader.readLine())!=null) {
 Integer id = Integer.parseInt(sLine);
 articleIds.add(id);
 }

 buffReader.close();

 return articleIds;

 }

 /**
 * Affiche le mode d'emploi de la ligne de commande
 */
 public static void displayUsage() {
 System.out.println("Arguments:");
 System.out.println("<Chemin fichier source id articles> <Chemin de destination>");
 System.out.println("");
 System.out.println("Notes:");
 System.out.println("Le fichier source des id articles doit être un fichier " +
 "comportant la liste des identifiants articles Wikipédia à récupérer, " +
 "ces identifiants doivent être séparés par une fin de ligne");
 System.out.println("Le chemin de destination doit exister.");

 }

 /**
 * Nettoyage du texte.
 * Les références du type [12], [3] etc... sont supprimées.
 *
 * @param text Texte à nettoyer
 * @return Texte nettoyé
 */
 public static String cleanup(String text) {
 return text.replaceAll("\\[[0-9]+\\]", "");
 }

 /**
 * Entrée principale
 *
 * @param args Arguments
 */
 public static void main(String[] args) {
 try {

 // Taille en caractères UTF-16 des fichiers de sortie
 // 1 Millions ~= 2 Mo
 //
 final int fileSizeLimit = 1000000;

 // Taille courante du fichier de sortie
 // mise à MAX + 1 afin qu'il soit créé lors du premier passage
 //
 int currentFileSize = fileSizeLimit + 1;

 // Url de récupération des articles Wikipédia par leur identifant article
 //
 String sWikiUrlFormat = "http://fr.wikipedia.org/w/index.php?action=render&curid=%d";

 FileOutputStream outStream = null;
 OutputStreamWriter writer = null;

 if (args.length != 2) {
 displayUsage();

 }
 else {

 // Récupération de la liste des identifiants articles à partir du fichier
 //
 List<Integer> articleIds = loadArticleIdsFromFile(args[0]);

 // Format de destination des fichiers du contenu de wikipédia
 //
 String sDestinationDir = args[1];

 // Pour chaque identifiant d'article
 //
 for (Integer articleId : articleIds) {

 try {

 // Si la taille courant du fichier de sortie est supérieure
 // à la taille max on en crée un nouveau
 //
 if (currentFileSize > fileSizeLimit) {
 if (writer != null) {
 writer.close();
 }
 outStream = new FileOutputStream(sDestinationDir + "\\" + articleId + ".txt");
 writer = new OutputStreamWriter(outStream, "UTF-16");

 currentFileSize = 0;

 }

 // Construction de l'url de l'article
 //
 String sWikiUrl = String.format(sWikiUrlFormat, articleId);

 // Récupération du contenu de l'article
 //
 String sWiki = dumpWikiXhtmlToString(sWikiUrl, "UTF-8");

 // Chargement XML du contenu
 //
 InputSource is = new InputSource(new StringReader(sWiki));
 Document doc = DocumentBuilderFactory.newInstance()
 .newDocumentBuilder()
 .parse(is);

 //writer.write("========= " + articleId + " ==========\n");

 // Ciblage du contenu à récupérer via XPath
 //
 NodeList nl = XPathProcessor.getInstance().EvaluateMutli(
 "/WikiContent/p", doc);

 // Pour chaque noeud
 //
 for (int i = 0; i < nl.getLength(); i++) {
 String sContent = "";
 try {
 Node node = nl.item(i);

 // Récupération du texte
 //
 sContent = node.getTextContent();

 // Nettoyage
 //
 sContent = cleanup(sContent);

 // Ecriture dans le fichier
 //
 writer.write(sContent);
 writer.write("\n\n");
 writer.flush();

 // Augmentation de la taille du fichier courant
 //
 currentFileSize += sContent.length() + 2;
 }
 catch (Exception e2) {
 String sLog = "Impossible de récupérer le paragraphe " + i + " pour id=" + articleId;
 System.out.println(sLog);

 }
 }
 }
 catch (Exception e3) {
 String sLog = "Impossible de récupérer l'article id=" + articleId + "\n";
 System.out.println(sLog);

 e3.printStackTrace();
 }

 }
 writer.close();
 }
 }

 catch (Exception e) {
 e.printStackTrace();

 }

 }

}

Cet utilitaire nécessite une classe de gestion des requêtes XPath, dont le code est à cette adresse.

Une version déjà compilée de WikiDump est disponible à cette adresse.

Récupérer le contenu de Wikipedia

Saturday, June 26th, 2010

Wikipedia est une gigantesque source de contenu pour celui qui a besoin d’ une grand quantité de données en format texte.

Récupérer ces données est autorisé, Wikipedia fourni même des moyens pour récupérer tous styles de données ainsi que de la documentation (sommaire et fouillie) à cette adresse.

Mon expérience personnelle permet de faire les constats suivants:

  • Il est difficile de parser le balisage propre à Wiki (Wikicode ou Wikitext), même si de nombreux outils existent en Java ( Mylyn etc..) la présence de templates spécifiques Wikipedia rend certaines substitutions impossibles simplement.
  • Le meilleur parser de Wikitext est Wikipedia lui même…
  • Installer un mirroir exact de Wikipedia en local est long fastidieux et assez mal documenté
  • La récupération des articles parsés directement par Wikipedia à 1 article par seconde ne pose pas de problème à Wikipedia, mais le processus est très long (1 article = 1 sec <=> 100000 articles = 1 jour)
  • Les articles doivent être filtrés car on a de tout, le meilleurs comme le pire

J’ai pu récupérer les données de Wikipedia de la manière suivante:

  1. Pré requis
    Serveur et outils MySQL
    Machine virtuelle java (le chemin de java.exe doit être dans la variable d’environnement PATH)
    L’utilitaire maison java WikiDump [ Sources - Version compilée ] (dans la version compilée un batch est fourni pour lancer une démo, l’exception qui apparait est voulue, elle n’interrompt pas le traitement )
  2. Se rendre sur le site http://dumps.wikimedia.org/frwiki/latest/ afin de télécharger les dumps MySQL:
    frwiki-latest-category.sql.gz
    : Contenant la liste des catégories et le nombre d’articles qui s’y ratache.
    frwiki-latest-categorylinks.sql.gz
    : Permettant de faire le lien entre les articles et les catégories.
  3. Importer les scripts dans une base de données MySQL (sous Windows j’ai utilisé MySQL Administrator).
    Note: Le processus est relativement long (3 heures environ).
  4. Filtrer les articles voulus en lançant la requête suivante afin de récupérer les catégories Portail : SELECT * FROM category WHERE cat_pages > 0 AND cat_title LIKE “Portail:%” ORDER BY cat_pages DESC

    Une fois les portails choisis (A prendre et A refuser), il est nécessaire d’en sélectionner les articles. Lancer la requête suivante en mettant votre propre liste de portails:

    SELECT cl_data_in.cl_from FROM ((SELECT DISTINCT cl3.cl_from FROM categorylinks AS cl3 WHERE cl3.cl_to IN ("Portail:APrendre1,Portail:APrendre2")) AS cl_data_in)
    WHERE cl_data_in.cl_from NOT IN (SELECT DISTINCT cl2.cl_from FROM categorylinks AS cl2 WHERE LOWER(cl2.cl_to LIKE "liste d%") OR cl2.cl_to IN ("Homonymie","Portail:ARefuser1"))

    Une fois la liste des articles récupérés les exporter dans un fichier csv ou autre, le format importe peu, l’essentiel est d’avoir un identifiant par ligne.

    Nettoyer le fichier en supprimant le nom de colonne et les ligne vides.

  5. Pour lancer la récupération du contenu, dans la ligne de commande, tapper:java -jar WikiDump.jar Chemin_fichier_articles Repertoire_de_destination > log.txt

Une fois le dernier (long) traitement effectué, vous avez pu récupérer une quantité de données au delà de vos espérances.

Par courtoisie, si vous effectuez un gros ramassage, n’oubliez pas de faire à un don à Wikipedia.

Java XPath cache query

Saturday, June 26th, 2010

See post in french.

When we want to process multiple identical XPath query on XML document, in a optimized way, we have to compile only once XPath query and to store it to be able to use it later.

Le following singleton Java class manage in a simple way cached XPath query:


package com.devbypractice;

import java.util.HashMap;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class XPathProcessor
{
 // Singleton instance
 //
 protected static XPathProcessor _instance = new XPathProcessor();

 // XPath expressions hashmap cache
 //
 protected HashMap<String, XPathExpression> _hmXPathExpressions = new HashMap<String, XPathExpression>();

 // Xpath processing object
 //
 XPath xPath = XPathFactory.newInstance().newXPath();

 /**
 * Private constructor
 */
 protected XPathProcessor() {}

 /**
 * @return Singleton instance
 */
 public static XPathProcessor getInstance()
 {
 return _instance;
 }

 /**
 * Get XPath compiled expression from cache
 *
 * @param sXpathExpress XPath string
 * @return XPath compiled expression
 * @throws Exception
 */
 protected  XPathExpression getExpression(String sXpathExpress) throws Exception
 {
 XPathExpression expression;
 if (_hmXPathExpressions.containsKey(sXpathExpress)) {
 expression = _hmXPathExpressions.get(sXpathExpress);
 }
 else {
 expression = xPath.compile(sXpathExpress);
 _hmXPathExpressions.put(sXpathExpress, expression);
 }

 return expression;
 }

 /**
 * Evaluate XPath expression on Xml with single result
 * @param sXpathExpress XPath string
 * @param xml XML node
 * @return Single result of XPath evaluation
 * @throws Exception
 */
 public String Evaluate(String sXpathExpress, Node xml) throws Exception
 {
 XPathExpression expr = getExpression(sXpathExpress);

 return expr.evaluate(xml);
 }

 /**
 * Evaluate XPath expression on Xml with multiple results
 * @param sXpathExpress XPath string
 * @param xml XML node
 * @return Multiple results of XPath evaluation
 * @throws Exception
 */
public NodeList EvaluateMutli(String sXpathExpress, Node xml) throws Exception
 {
 XPathExpression expr = getExpression(sXpathExpress);

 return (NodeList)expr.evaluate(xml, XPathConstants.NODESET);
 }

}

Usage:

  • XPathProcessor.getInstance().Evaluate(“/xpath1″, xmlNode1)
  • XPathProcessor.getInstance().EvaluateMulti(“/xpath2″, xmlNode2)

Cache de requêtes XPath Java

Saturday, June 26th, 2010

Voir l’article en anglais.

Lorsque l’on veut lancer de multiples requêtes XPath identiques sur des documents XML, et ce de manière optimisée, il est nécessaire de ne compiler qu’une seule fois la requête XPath et de la stoker  afin de pouvoir la réutiliser plus tard.

La classe singleton Java suivante permet de gérer de manière simple un cache de requêtes XPath compilées:


package com.devbypractice;

import java.util.HashMap;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * Classe d'évaluation d'expressions XPath
 *
 */
public class XPathProcessor
{
 // Instance du singleton
 //
 protected static XPathProcessor _instance = new XPathProcessor();

 // Hashmap du cache d'expressions Xpath
 //
 protected HashMap<String, XPathExpression> _hmXPathExpressions = new HashMap<String, XPathExpression>();

 // Objet de traitement Xpath
 //
 XPath xPath = XPathFactory.newInstance().newXPath();

 /**
 * Constructeur privé
 */
 protected XPathProcessor() {}

 /**
 * @return L'instance du singleton
 */
 public static XPathProcessor getInstance()
 {
 return _instance;
 }

 /**
 * Récupération de l'expression XPath compilée à partir du cache
 *
 * @param sXpathExpress L'expression XPath sour forme de chaine
 * @return L'expression XPath compilée
 * @throws Exception
 */
 protected  XPathExpression getExpression(String sXpathExpress) throws Exception
 {
 XPathExpression expression;
 if (_hmXPathExpressions.containsKey(sXpathExpress)) {
 expression = _hmXPathExpressions.get(sXpathExpress);
 }
 else {
 expression = xPath.compile(sXpathExpress);
 _hmXPathExpressions.put(sXpathExpress, expression);
 }

 return expression;
 }

 /**
 * Evaluation d'une expression XPath sur un document XML
 * @param sXpathExpress Expression XPath sous forme de chaine
 * @param xml XML parsé
 * @return Le résultat unique de l'évaluation Xpath
 * @throws Exception
 */
 public String Evaluate(String sXpathExpress, Node xml) throws Exception
 {
 XPathExpression expr = getExpression(sXpathExpress);

 return expr.evaluate(xml);
 }

 /**
 * @param sXpathExpress Expression XPath sous forme de chaine
 * @param xml XML parsé
 * @return Le résultat multiple de l'évaluation Xpath
 * @throws Exception
 */
public NodeList EvaluateMutli(String sXpathExpress, Node xml) throws Exception
 {
 XPathExpression expr = getExpression(sXpathExpress);

 return (NodeList)expr.evaluate(xml, XPathConstants.NODESET);
 }

}

A l’utilisation, il suffit d’exécuter:

  • XPathProcessor.getInstance().Evaluate(“/xpath1″, xmlNode1) pour obtenir un résultat unique d’une requête XPath
  • XPathProcessor.getInstance().EvaluateMulti(“/xpath2″, xmlNode2) afin de récupérer de multiples résultats de l’exécution de la requête XPath.

Script Ant de génération de certificats PKS X.509 autosignés pour service et client

Sunday, May 16th, 2010

Voir l’article en anglais.

Dans le cadre des Web Services et WS-Security, si l’on veut signer et crypter les requêtes SOAP entre le client et le service les certificats PKS X.509 sont appropriés.

Le certificat (ou keystore) du service doit contenir une clé privée afin de déchiffrer les données du client et la clé publique du client afin de vérifier sa signature.
Le certificat du client doit contenir une clé privée pour signer le message et la clé publique du service pour chiffrer le contenu du message.

Java KeyTool est un utilitaire fourni avec le JDK qui permet de générer ce type de certificats, néanmoins les étapes sont nombreuses pour générer les keystores services et clients:

  • générer le keystore du service
  • autosigner le keystore du service
  • exporter la clé publique du service
  • générer le keystore client
  • autosigner le keystore client
  • exporter la clé publique client
  • importer la clé publique client dans le keystore du service
  • importer la clé publique du service dans le keystore client

Afin de pouvoir générer ces certificats de manière automatisée et ce sur de multiples plateformes, voici le script Ant qui s’occupe de tout (l’accès à l’utilitaire keytool doit être dans le path système):


<project basedir="." name="AntKeyTool">

 <!-- Path -->
 <property name="src.dir" value="${basedir}/src"/>
 <property name="key.dir" value="${src.dir}/security"/>

 <!-- generateKey config-->
 <!-- Service config -->
 <property name="serviceKeystore.file" value="${key.dir}/service.jks"/>
 <property name="servicePublicKey.file" value="${key.dir}/service_public.key"/>
 <property name="serviceKeystore.alias" value="service"/>
 <property name="serviceKeystore.password" value="servicepwd"/>
 <property name="serviceKeystore.dname" value='"CN=wss, OU=orgUnit, O=company, L=city, ST=state, C=us"'/>

 <!-- Client config -->
 <property name="clientKeystore.file" value="${key.dir}/client.jks"/>
 <property name="clientPublicKey.file" value="${key.dir}/client_public.key"/>
 <property name="clientKeystore.alias" value="client"/>
 <property name="clientKeystore.password" value="clientpwd"/>
 <property name="clientKeystore.dname" value='"CN=wss, OU=orgUnit, O=company2, L=city, ST=state, C=us"'/>

 <!-- Macro to genererate basic key -->
 <macrodef name="generateKey.macro">
 <attribute name="keystore.file"/>
 <attribute name="publicKey.file"/>
 <attribute name="keystore.alias"/>
 <attribute name="keystore.password"/>
 <attribute name="keystore.dname"/>
 <sequential>

 <!-- Keystore creation -->
 <exec executable="keytool">
 <arg value="-genkey"/>
 <arg value="-keyalg"/>
 <arg value="RSA"/>
 <arg value="-keysize"/>
 <arg value="1024"/>
 <arg value="-alias"/>
 <arg value="@{keystore.alias}"/>
 <arg value="-keystore"/>
 <arg value="@{keystore.file}"/>
 <arg value="-storepass"/>
 <arg value="@{keystore.password}"/>
 <arg value="-keypass"/>
 <arg value="@{keystore.password}"/>
 <arg value="-dname"/>
 <arg value="@{keystore.dname}"/>
 </exec>

 <!-- self sign -->
 <exec executable="keytool">
 <arg value="-selfcert"/>
 <arg value="-alias"/>
 <arg value="@{keystore.alias}"/>
 <arg value="-keystore"/>
 <arg value="@{keystore.file}"/>
 <arg value="-storepass"/>
 <arg value="@{keystore.password}"/>
 </exec>

 <!-- Public key export -->
 <exec executable="keytool">
 <arg value="-export"/>
 <arg value="-alias"/>
 <arg value="@{keystore.alias}"/>
 <arg value="-keystore"/>
 <arg value="@{keystore.file}"/>
 <arg value="-storepass"/>
 <arg value="@{keystore.password}"/>
 <arg value="-file"/>
 <arg value="@{publicKey.file}"/>
 </exec>
 </sequential>
 </macrodef>

 <!-- Macro to import public key -->
 <macrodef name="importPublicKey.macro">
 <attribute name="keystore.file"/>
 <attribute name="publicKey.file"/>
 <attribute name="keystore.alias"/>
 <attribute name="keystore.password"/>
 <sequential>
 <exec executable="keytool">
 <arg value="-import"/>
 <arg value="-noprompt"/>
 <arg value="-trustcacerts"/>
 <arg value="-alias"/>
 <arg value="@{keystore.alias}"/>
 <arg value="-keystore"/>
 <arg value="@{keystore.file}"/>
 <arg value="-storepass"/>
 <arg value="@{keystore.password}"/>
 <arg value="-file"/>
 <arg value="@{publicKey.file}"/>
 </exec>
 <delete file="@{publicKey.file}"/>
 </sequential>
 </macrodef>

 <!-- Ant Target de to generate service and client keystores -->
 <target name="generateKeys">
 <mkdir dir="${key.dir}"/>
 <delete file="${serviceKeystore.file}"/>
 <delete file="${clientKeystore.file}"/>

 <!-- Service self signed keystore creation -->
 <generateKey.macro keystore.file="${serviceKeystore.file}"
 publickey.file="${servicePublicKey.file}"
 keystore.alias="${serviceKeystore.alias}"
 keystore.password="${serviceKeystore.password}"
 keystore.dname="${serviceKeystore.dname}"
 />

 <!-- Client self signed keystrore creation -->
 <generateKey.macro keystore.file="${clientKeystore.file}"
 publickey.file="${clientPublicKey.file}"
 keystore.alias="${clientKeystore.alias}"
 keystore.password="${clientKeystore.password}"
 keystore.dname="${clientKeystore.dname}"
 />

 <!-- Import client public key in service keystore -->
 <importPublicKey.macro keystore.file="${serviceKeystore.file}"
 publickey.file="${clientPublicKey.file}"
 keystore.alias="${clientKeystore.alias}"
 keystore.password="${serviceKeystore.password}"
 />

 <!-- Import service public key in client keystore -->
 <importPublicKey.macro keystore.file="${clientKeystore.file}"
 publickey.file="${servicePublicKey.file}"
 keystore.alias="${serviceKeystore.alias}"
 keystore.password="${clientKeystore.password}"
 />

 </target>

</project>

Anciennes JVM : JRE, JDK et plus d’archives du monde Java

Sunday, May 16th, 2010

Voir l’article en anglais.

Les anciennes JVM : JRE, JDK et autres outils Java sont disponibles à l’adresse suivante:

http://java.sun.com/products/archive/

C’est un vrai musée des antiquités et un bookmark indispensable afin de reproduire la configuration Java des clients.

Ant script to generate self signed X.509 PKS certificates for service and client

Thursday, May 13th, 2010

See post in french.

In case of Web Service and WS-Security, if you want to sign and crypt SOAP requests beetween client and service you can use X.509 PKS certificates.

Service’s certificate or keystrore must contain a private key to decrypt data from client and client’s public key to check signature.
Client’s certificate must contain a private key to sign data and service’s public key to crypt message content.

Java KeyTool is an utility provided with JDK wich allow you to generate such certificates but there are multiple steps to produce service and client keystores:

  • generate service keystore
  • self sign service keystore
  • export service’s public key
  • generate client keystore
  • self sign client keystore
  • export client’s  public key
  • import client’s public key in service keystore
  • import service public key in client keystore

Here is an ant script which do it all (keytool must be in system path):


<project basedir="." name="AntKeyTool">

 <!-- Path -->
 <property name="src.dir" value="${basedir}/src"/>
 <property name="key.dir" value="${src.dir}/security"/>

 <!-- generateKey config-->
 <!-- Service config -->
 <property name="serviceKeystore.file" value="${key.dir}/service.jks"/>
 <property name="servicePublicKey.file" value="${key.dir}/service_public.key"/>
 <property name="serviceKeystore.alias" value="service"/>
 <property name="serviceKeystore.password" value="servicepwd"/>
 <property name="serviceKeystore.dname" value='"CN=wss, OU=orgUnit, O=company, L=city, ST=state, C=us"'/>

 <!-- Client config -->
 <property name="clientKeystore.file" value="${key.dir}/client.jks"/>
 <property name="clientPublicKey.file" value="${key.dir}/client_public.key"/>
 <property name="clientKeystore.alias" value="client"/>
 <property name="clientKeystore.password" value="clientpwd"/>
 <property name="clientKeystore.dname" value='"CN=wss, OU=orgUnit, O=company2, L=city, ST=state, C=us"'/>

 <!-- Macro to genererate basic key -->
 <macrodef name="generateKey.macro">
 <attribute name="keystore.file"/>
 <attribute name="publicKey.file"/>
 <attribute name="keystore.alias"/>
 <attribute name="keystore.password"/>
 <attribute name="keystore.dname"/>
 <sequential>

 <!-- Keystore creation -->
 <exec executable="keytool">
 <arg value="-genkey"/>
 <arg value="-keyalg"/>
 <arg value="RSA"/>
 <arg value="-keysize"/>
 <arg value="1024"/>
 <arg value="-alias"/>
 <arg value="@{keystore.alias}"/>
 <arg value="-keystore"/>
 <arg value="@{keystore.file}"/>
 <arg value="-storepass"/>
 <arg value="@{keystore.password}"/>
 <arg value="-keypass"/>
 <arg value="@{keystore.password}"/>
 <arg value="-dname"/>
 <arg value="@{keystore.dname}"/>
 </exec>

 <!-- self sign -->
 <exec executable="keytool">
 <arg value="-selfcert"/>
 <arg value="-alias"/>
 <arg value="@{keystore.alias}"/>
 <arg value="-keystore"/>
 <arg value="@{keystore.file}"/>
 <arg value="-storepass"/>
 <arg value="@{keystore.password}"/>
 </exec>

 <!-- Public key export -->
 <exec executable="keytool">
 <arg value="-export"/>
 <arg value="-alias"/>
 <arg value="@{keystore.alias}"/>
 <arg value="-keystore"/>
 <arg value="@{keystore.file}"/>
 <arg value="-storepass"/>
 <arg value="@{keystore.password}"/>
 <arg value="-file"/>
 <arg value="@{publicKey.file}"/>
 </exec>
 </sequential>
 </macrodef>

 <!-- Macro to import public key -->
 <macrodef name="importPublicKey.macro">
 <attribute name="keystore.file"/>
 <attribute name="publicKey.file"/>
 <attribute name="keystore.alias"/>
 <attribute name="keystore.password"/>
 <sequential>
 <exec executable="keytool">
 <arg value="-import"/>
 <arg value="-noprompt"/>
 <arg value="-trustcacerts"/>
 <arg value="-alias"/>
 <arg value="@{keystore.alias}"/>
 <arg value="-keystore"/>
 <arg value="@{keystore.file}"/>
 <arg value="-storepass"/>
 <arg value="@{keystore.password}"/>
 <arg value="-file"/>
 <arg value="@{publicKey.file}"/>
 </exec>
 <delete file="@{publicKey.file}"/>
 </sequential>
 </macrodef>

 <!-- Ant Target de to generate service and client keystores -->
 <target name="generateKeys">
 <mkdir dir="${key.dir}"/>
 <delete file="${serviceKeystore.file}"/>
 <delete file="${clientKeystore.file}"/>

 <!-- Service self signed keystore creation -->
 <generateKey.macro keystore.file="${serviceKeystore.file}"
 publickey.file="${servicePublicKey.file}"
 keystore.alias="${serviceKeystore.alias}"
 keystore.password="${serviceKeystore.password}"
 keystore.dname="${serviceKeystore.dname}"
 />

 <!-- Client self signed keystrore creation -->
 <generateKey.macro keystore.file="${clientKeystore.file}"
 publickey.file="${clientPublicKey.file}"
 keystore.alias="${clientKeystore.alias}"
 keystore.password="${clientKeystore.password}"
 keystore.dname="${clientKeystore.dname}"
 />

 <!-- Import client public key in service keystore -->
 <importPublicKey.macro keystore.file="${serviceKeystore.file}"
 publickey.file="${clientPublicKey.file}"
 keystore.alias="${clientKeystore.alias}"
 keystore.password="${serviceKeystore.password}"
 />

 <!-- Import service public key in client keystore -->
 <importPublicKey.macro keystore.file="${clientKeystore.file}"
 publickey.file="${servicePublicKey.file}"
 keystore.alias="${serviceKeystore.alias}"
 keystore.password="${clientKeystore.password}"
 />

 </target>

</project>

Older JVM : JRE, JDK and more Java archives

Tuesday, May 11th, 2010

See post in french.

Older JVM : JRE, JDK and more can be found at:

http://java.sun.com/products/archive/

It’s a real museum of antiquities and a must have bookmark to be able to reproduce customer’s Java configurations.