samedi 7 décembre 2013

Client Server Bluetooth en java : le code commenté.

1. Préparation
2. Un peu de théorie sur bluetooth
3. Le code commenté
4. Le projet eclipse avec toutes les dépendances

Voici maintenant le code commenté du client et du server.

Le code client

package bluetooth;

import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.bluetooth.DeviceClass;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.DiscoveryListener;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.ServiceRecord;
import javax.bluetooth.UUID;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;

/**
 * Le code client.
 * 
 * Ce code est à executer sur l'ordinateur qui sert de client.
 * 
 * Ce code met en oeuvre les trois étapes que nous avons décrites précédemment : 
 * 1 On découvre les appareils autour de nous (inquiry)
 * 2 Recherche de service sur les appareils trouvés.
 * 3 Utilisation du service 
 * 
 * On remarque que l'api thread est mis en oeuvre ici car la méthode startInquiry 
 * démarre dans un nouveau thread (thread enfant) mais le thread père doit disposer d'un
 * moyen de savoir quand le thread enfant à fini. Pour faire cela on utilise la technique dite du 
 * sémaphore. Si vous ne la connaissez pas je vous conseille de commencer par lire ces quelques explications :
 *  
 * http://www.javaworld.com/javaqa/1999-11/02-qa-semaphore.html.
 * 
 *  
 */
public class Client {

 /**
  * La liste des devices bluetooth trouvés.
  */
 public static final List devicesDiscovered = new ArrayList();

 /**
  * Le service que je recherche sur ce device, un service doit avoir un nom unique, on utilise 
  * un utilitaire uuidgen pour générer ces chaines de caractère uniques.
  * 
  * Sur le code du serveur c'est aussi cette chaine qui est utilisés pour publier le service.
  *  
  */
 final static String myServiceUUID = "2d26618601fb47c28d9f10b8ec891363";
 
 /**
  * L'uid doit respecter certaine règle comme par exemple être positif la chaine doit faire 
  * moins de 32 caractère etc. La classe UUID contrôle ces règles lors de la construction.
  */
 final static UUID MYSERVICEUUID_UUID = new UUID(myServiceUUID, false);
 
 /**
  * La liste des services que l'on souhaite obtenir sur le devices. 
  */
 final static UUID[] searchUuidSet = new UUID[] { MYSERVICEUUID_UUID };
 
 /**
  * Lorsque l'on obtient le service, à la demande du client le serveur peut fournir 
  * des sortes de métadonnées (pair clé-valeur) appelés attributs. On pourrait imaginer un client 
  * qui voudrait envoyer de la musique à une chaine stéréro souhaiterai connaitre la liste des
  * formats musicaux supportés par le services. Sans spécificiation une liste d'attributs par défaut est fourni.
  * @see javax.bluetooth.DiscoveryAgent.searchServices(int[] attrSet, UUID[] uuidSet, RemoteDevice btDev, DiscoveryListener discListener)
  */
 final static int[] attrIDs = new int[] { 0x0100 }; // Service handle
 
 public static void main(String[] args) throws IOException,
   InterruptedException {

  
  //inquiryCompletedEvent est un semaphore 
  //qui bloquera le thread a l'appel de sa méthode wait 
  //et qui le poursuivra a l'appel de sa méthode notifyAll
  final Object inquiryCompletedEvent = new Object();
  
  //serviceSearchCompletedEvent est un semaphore 
  //qui bloquera le thread a l'appel de sa méthode wait 
  //et qui le poursuivra a l'appel de sa méthode notifyAll
  final Object serviceSearchCompletedEvent = new Object();

  // on vide la listes des périphériques bluetooth que l'on va trouver
  devicesDiscovered.clear();

  //le listener qui déclenchera les actions quand des devices et les services seront trouvés.
  DiscoveryListener listener = new MyDiscoveryListener(devicesDiscovered, inquiryCompletedEvent, serviceSearchCompletedEvent);
  
  
  //dabord on cherche les devices 
  synchronized (inquiryCompletedEvent) {
   // on passe le listener à la méthode start inquiry, cette appel 
   // revient tout de suite car il a lieu dans un autre thread.
   boolean started = LocalDevice.getLocalDevice().getDiscoveryAgent()
     //GIAC correspond au profil générique.
     .startInquiry(DiscoveryAgent.GIAC, listener);
   if (started) {
    System.out.println("wait for device inquiry to complete...");
    // wait met en attente le thread principal qui attend 
    // maintenant qu'un autre thread le notifie.
    inquiryCompletedEvent.wait();
    System.out.println(devicesDiscovered.size()
      + " device(s) found");    
   }
  }
  
    //une fois les devices trouvés on cherche les services sur ces devices
    for (RemoteDevice rm : devicesDiscovered){
   synchronized (serviceSearchCompletedEvent) {
    System.out.println("search services on " +  rm.getBluetoothAddress() + " " + rm.getFriendlyName(false));
             LocalDevice.getLocalDevice().getDiscoveryAgent().searchServices(attrIDs, searchUuidSet, rm, listener);
    serviceSearchCompletedEvent.wait();
    System.out.println("Fin de la recherche de services");
   }
  }
  
  
  
  
 }

}

/**
 * on cree un listener que l'on passera à la méthode startInquiry pour
 * mieux comprendre le pattern listener
 * http://rom.developpez.com/java-listeners/.
 * Ce listener sera notifié a
 * chaque fois qu'un device sera découvert. Notifié signifie en fait que
 * ses méthodes seront appelées à chaque fois que le système découvrira
 * un device. On va donc redéfinir les méthodes pour mettre en oeuvre
 * nos actions à chaque dévouverte.
 * 
 * Les méthodes deviceDiscovered et inquiryCompleted du listener s'applique lors de l'inquiry,
 * alors que les méthodes servicesDiscovered et serviceSearchCompleted s'applique 
 * lors de la recherche de service sur un device. Je pense qu'ils auraient du créer 
 * deux listeners car ces deux actions sont quand même différentes. 
 * 
 *
 */
class MyDiscoveryListener implements DiscoveryListener {
 
 private List devicesDiscovered;
 
 private Object inquiryCompletedEvent;
 
 private Object serviceSearchCompletedEvent;
 
 
 /**
  * 
  * @param devicesDiscovered la liste des devices trouvés.
  * @param inquiryCompletedEvent le semaphore pour la recherhe de device.
  * @param serviceSearchCompletedEvent le semaphore pour la recherche de service.
  */
 public MyDiscoveryListener(List devicesDiscovered, Object inquiryCompletedEvent, Object serviceSearchCompletedEvent){
  this.devicesDiscovered = devicesDiscovered;
  this.inquiryCompletedEvent = inquiryCompletedEvent;
  this.serviceSearchCompletedEvent = serviceSearchCompletedEvent;
 }

 /**
  * Méthode qui sera appelee à chaque fois qu'un device est decouvert.
  */
 public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) {
  System.out.println("Device " + btDevice.getBluetoothAddress()
    + " found");
  // on alimente la liste des devices trouvés pour pouvoir
  // exploiter
  // cette information ultérieurement.
  devicesDiscovered.add(btDevice);
  try {
   System.out.println("     name "
     + btDevice.getFriendlyName(false));
   System.out.println("Blutooth Adress "  + btDevice.getBluetoothAddress());
  } catch (IOException cantGetDeviceName) {
   cantGetDeviceName.printStackTrace();
  }
 }

 /**
  * Méthode appelée quand le système estime avoir fini son travail
  * d'inquiry pour nous cela signifie que notre liste de device est
  * complète.
  */
 public void inquiryCompleted(int discType) {
  if (INQUIRY_COMPLETED==discType){
   System.out.println("Device Inquiry completed!");
   synchronized (inquiryCompletedEvent) {
    //on notifie cette objet pour permettre 
    //au thread principal de revenir de sa méthode wait.
    inquiryCompletedEvent.notifyAll();
   }
  }else if (INQUIRY_ERROR==discType){
   System.out.println("inquiry request failed to complete normally, but was not cancelled.");
  }else if (INQUIRY_TERMINATED==discType){
   System.out.println("device discovery has been canceled by the application and did not complete.");
  }
 }

 

 /**
  * Quand l'ensemble des services publiés que vous avez souhaité
  * retrouver sont découverts cette méthode est appelée.
  * 
  * A chaque service correspond un service record c'est pourquoi 
  * on reçoit un tableau, qui correspond au tableau de service que vous avez demandé 
  * lors de l'appel de  javax.bluetooth.DiscoveryAgent.searchServices(int[] attrSet, UUID[] uuidSet, RemoteDevice btDev, DiscoveryListener discListener)
  * 
  * Si sur le device serveur vous n'avez publié 
  * qu'un seul service vous  recevrez logiquement un tableau avec un seul élément.
  * 
  * Au sein de ces services record se trouve des serviceAttribute. 
  * Les attributs par défaut sont fourni mais il est possible de 
  * demander à recevoir d'autres attributs. 
  * 
  * @see javax.bluetooth.DiscoveryAgent.searchServices(int[] attrSet, UUID[] uuidSet, RemoteDevice btDev, DiscoveryListener discListener)
  * 
  */
 public void servicesDiscovered(int transID,
   ServiceRecord[] servRecords) {
      
  for (ServiceRecord sr : servRecords){
   System.out.println("Service trouvé " + sr.getConnectionURL(ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false) );
   
   //je vais maintenant pouvoir parler au sevice !!!!
   System.out.println("Envoie du message en cours");
   //on récupère l'url du service 
   //on pourrait sauvegarder cette url 
   //pour un acces direct au service sans passer
   //par la phase d'Inquiry
         String connectionURL =  sr.getConnectionURL (
                 ServiceRecord.NOAUTHENTICATE_NOENCRYPT, false);
         try {
             System.out.println(
                 "Connection a " + sr.getHostDevice().getFriendlyName(false) + 
                 ", " + connectionURL);
             StreamConnection streamConnection =  (StreamConnection) Connector.open (connectionURL);
             DataOutputStream dataout = 
                 streamConnection.openDataOutputStream();
             //maintenant j'envoie hello world au serveur.
             dataout.writeUTF("Hello World");
             System.out.println("Message envoye, fermeture du canal");
             streamConnection.close();
         } catch (IOException ioe) {
             System.out.println(
                 " exception & + ioe");
         }
  }
 }
 
 /*
  * Cette méthode sera appelée quand la recherche de service 
  * sera finie. Comme dans cette exemple on parle immédiatement 
  * au service dès qu'on le trouve. elle ne nous sera pas d'une 
  * grande utilité.
  *  
  * @see javax.bluetooth.DiscoveryListener#serviceSearchCompleted(int, int)
  */
 public void serviceSearchCompleted(int transID, int respCode) {
  System.out.println("Service search completed!");
  synchronized (serviceSearchCompletedEvent) {
   //on notifie cette objet pour permettre 
   //au thread principal de revenir de sa méthode wait.
   serviceSearchCompletedEvent.notifyAll();
  }
 }
}


Le code Serveur

package bluetooth;

import java.io.DataInputStream;
import java.io.IOException;

import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.RemoteDevice;
import javax.bluetooth.UUID;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
import javax.microedition.io.StreamConnectionNotifier;

/**
 * C'est un serveur bluetooth il ne fait que répéter dans la console 
 * le message qu'il a reçu.
 * 
 * Ce code est a executer sur l'ordinateur qui sert de serveur.
 * 
 * @author michael
 *
 */
public class Server {
 
 public static void main(String[] args) throws IOException {
  // le nom du service car l'uuid n'est pas très parlant ...
  final String myServiceName = "echoService";
  /**
   * Le service que je publie sur ce device, un service doit avoir un nom unique, on utilise 
   * un utilitaire uuidgen pour générer ces chaines de caractère uniques.
   * 
   * Sur le code du client c'est aussi cette chaine qui est utilisés pour obtenir le service.
   *  
   */
  final String myServiceUUID = "2d26618601fb47c28d9f10b8ec891363";
  UUID MYSERVICEUUID_UUID = new UUID(myServiceUUID, false);

  // Définit l'url du service.
  //localhost car on est le serveur 
  String connURL = "btspp://localhost:"+MYSERVICEUUID_UUID.toString()+";name="+myServiceName;
 
  //on se rend découvrable 
  LocalDevice.getLocalDevice().setDiscoverable(DiscoveryAgent.GIAC);
  
  System.out.println("Creation d'un service " + connURL);
  
  // On publie le service record dans le SRDB (Service record database)
  StreamConnectionNotifier scn = (StreamConnectionNotifier)  
                                           Connector.open(connURL);                   
 
  // On accepte la connexion d'un client, tant qu'aucun client 
  //ne frappe à la porte cette méthode bloque.
  StreamConnection sc = scn.acceptAndOpen();
  //on lit ce que le client nous envoie puis on l'écrit dans la console.
  DataInputStream dataIn = sc.openDataInputStream();
  String s = dataIn.readUTF();
  System.out.println("Echo > " + s);
  sc.close();
  scn.close();
 }
 
 

}

dimanche 17 novembre 2013

Client Server Bluetooth en java : un peu de théorie.

1. Préparation
2. Un peu de théorie sur bluetooth
3. Le code commenté
4. Le projet eclipse avec toutes les dépendances

Cette partie a pour but de voir le minimum necessaire sur la spécification bluetooth pour pouvoir comprendre les apis logiciels que nous mettons en oeuvre dans ce tutoriel. Ce résumé à ma sauce s'appuie sur :



Le dernier article est très complet et je vous conseille de l'approfondir pour étudier l'utilisation du JSR 82.

Le stack bleutooth 


Ce sont les couches logicielles qui nous interesse et ne parlerons pas des couches matérielles. 

L2CAP
L2CAP (Logical Link Control & Adaptation Protocol) fournit les services de multiplexage des protocoles de niveau supérieur et la segmentation et le réassemblage des paquets ainsi que le transport des informations de qualité de service. Les protocoles de haut niveau peuvent ainsi transmettre et recevoir des paquets jusqu'à 64 Ko. Elle autorise un contrôle de flux par canal de communication.

La couche L2CAP utilise des canaux logiques.
Dans le java bluetooth stack cette couche est représenté par l'interface L2CAPConnection. Comme elle respecte le CLDC Generic Connection Framework (http://www.oracle.com/technetwork/systems/index-155711.html) elle est accompagnée de L2CAPConnectionNotifier qui permet de créer des connections de type server. 

En pratique on ne travaillera pas directement avec L2CAPConnection mais plustôt avec l'interface StreamConnection et StreamConnectionServer qui respecte les mêmes contrats. En effet travailler L2CAPConnection nous oblige à limiter la taille de nos paquets et à gérer le réassemblage de paquets. Néanmoins voici un exemple d'utilisation de des interfaces.   
//coté client 
//.....
 L2CAPConnection conn = (L2CAPConnection) 
//on suppose que le server écoute sur le port 1003 on verra qu'en fait les services 
//sont découverts dynamiquement.
          Connector.open("btl2cap://0050CD00321B:1003;ReceiveMTU=512;TransmitMTU=512");
PrintStream out = new PrintStream(conn.openOutputStream());
//...

//coté serveur 
//.....
L2CAPConnectionNotifier service = (L2CAPConnectionNotifier) 
           Connector.open("btl2cap://localhost:0050CD00321B;ReceiveMTU=512;TransmitMTU=512");
L2CAPConnection conn = (L2CAPConnection) service.acceptAndOpen();
InputStream is = conn.openInputStream();


Comme nous le disions précédemment "Elle autorise un contrôle de flux par canal de communication" c'est pourquoi on trouve dans ces deux urls les parametres ReceiveMTU et TransmitMTU :

/**
 * ReceiveMTU
 * specifies the maximum payload size this connection can accept, and
 * TransmitMTU specifies the maximum payload size this connection can
 * send
*/



RFCOMM 

Il est exigé d'un stack bluetooth qu'au dessus de sa couche L2CAP un protocole série soit émulé : RFCOMM (Radio frequency communication).

Cette exigence connu aussi sous Serial Port Profile (SPP) existe car beaucoup de processus savent communiquer avec ce protocole plutôt qu'avec L2CAP.

Par exemple pour les tests sur un PC on peut utiliser Hyper Terminal pour voir les données arrivant sur le port émulé COM.

Je ne présente pas d'exemple d'utilisation avec RFCOMM car je pense que c'est la même chose que pour L2CAP. On trouve deux classes : BluetoothRFCommConnection et BluetoothRFCommConnectionNotifier qui implémentent respectivement StreamConnection et StreamConnectionNotifier que nous allons étudier plus en détail.

 Je n'ai néanmoins pas approfondi cette partie et toute remarque est bienvenue.

SDP 

Service Discovery protocole, qui permet de découvrir les autres devices à portée du client. Le code du précédent article présente ce processus et est déjà abondemment commenté.

TCS 

Telephony Control Protocol, est un protocole obsolète orienté donnée binaire, je n'ai pas cherché à l'étudier, si vous pouvez apporter plus de précision sur son rôle c'est avec plaisir que je lirai vos commentaires.

OBEX

 Obex est un protocole permettant l'échange d'objet, nous n'allons pas l'étudier dans ce tutoriel, mais vous trouverez deux exemples de codes : 

Client Obex : http://bluecove.org/apidocs/overview-summary.html#OBEXPutClient
Server Obex : http://bluecove.org/apidocs/overview-summary.html#OBEXPutServer

Les profils

Un profil correspond à une spécification fonctionnelle d'un usage particulier. Les profils peuvent également correspondre à différents types de périphériques.

Les profils ont pour but d'assurer une interopérabilité entre tous les appareils Bluetooth. Ils définissent : la manière d'implémenter un usage défini les protocoles spécifiques à utiliser les contraintes et les intervalles de valeurs de ces protocoles.

On a vu un de ces profils SPP (Serial Port Profile), lorsqu'un insdustriel souhaite obtenir la certification Bluetooth il doit valider une certaine quantités de ces profils.

La notion de client et de serveur.

Il faut bien différencier la notion de client et de serveur, un serveur publie un service et un client le consomme. Un device peut à la fois être client et serveur, aucun stack ne met de limite à être soit uniquement client soit uniquement serveur.

Voici un diagramme d'activité qui illustre ce que nos deux codes (code client et code serveur) vont faire.



Le code client.

Il va commencer par découvrir les devices bluetooth autour de lui : Un casque bluetooth, un téléphone, un autre ordinateur, etc.

C'est la phase d'inquiry, il va envoyer des ondes tout autour de lui pour dire qu'il cherche à découvrir les autres devices. Si les autres devices ont décidé de se rendre découvrable alors ils répondront à ce message d'inquiry pour dire qu'ils sont présents. Cela permet au client d'acquerir les adresses physiques des devices autour de lui et lui permet de les mettre dans son cache.

Enfin une fois qu'il aura découvert ces devices il va demander s'il existe un service spécifique sur le devices auquel il s'adresse. Si la réponse est positive alors la communication va avoir lieu.

Le code serveur

Il publie un service en ajoutant donc une entrée (Un service record) dans le SDB (Service Data Base) puis il se met en attente d'une requète entrante.

Et La sécurité et l'appairage des appareils ?


Cette partie n'est pas de la responsabilité des codes serveur ou client. C'est le statck sous-jacent qui doit s'occuper de l'appairage et de l'authentification des appareils entre eux.

Au mieux les codes peuvent exiger que l'échange n'ai lieu que si les appareils se sont authentifiés et/ou que l'échange soit cryptés, mais ce n'est pas eux qui  géreront cette partie.



lundi 4 novembre 2013

Client Server Bluetooth en Java : préparation.


1. Préparation
2. Un peu de théorie sur bluetooth
3. Le code commenté
4. Le projet eclipse avec toutes les dépendances

Cette article suppose que vous débutez en Java et sur Bluetooth et s'adresse à ceux qui souhaite avoir un tutoriel qui explique comment mettre en oeuvre un serveur et un client qui permettront à deux PC d'échanger des données.


Le code complet du projet eclipse illustrant ce tutoriel peut-être téléchargé ici, toutes les librairies sont incluses.
https://www.dropbox.com/s/e5r74khzt75wvbq/bluetooth.zip

Première étape, comprendre et vérifier le bon fonctionnement du  stack java bluetooth

Un stack bluetooth est une pile d'interface matériels puis logiciels permettant au logiciels utilisateurs de communiquer avec un device bluetooth. Plus on est haut dans la pile, plus le niveau d'abstraction est élevé.

Par exemple L2CAP est assez bas niveau et ressemble à un échange entre deux sockets, Obex permet l'échange de fichier, le profil main libre permet d'envoyer un appel téléphonique dans l'autoradio...

Nous reviendrons en partie 2 du tutoriel pour expliquer ces différentes couches et ce que l'on peut faire avec.

Différents fabricants ou éditeurs de logiciels (souvent des éditeurs d'OS) propose leur stack, parmi ceux-ci on peut citer les plus connus :
  • Broadcom (WIDCOMM)
  • Winsock (Microsoft)
  • BlueSoleil (IVT Corporation)
  • OS X (Universal Mac)
  • Linux BlueZ (D-Bus BlueZ API)
  • Linux BlueZ (historic BlueZ API)
Le stack java bluetooth est l'ensemble des api que que la plateforme java va mettre à votre disposition pour pouvoir écrire des programmes qui utiliseront ce stack natif sans avoir à vous préocuper du stack véritablement installé sur votre machine. En clair il va déléguer les appels à la librairie native qui sera installée sur votre poste.

En java les api d'utilisation du stack bluetooth sont défini par la JSR 82 : http://www.jcp.org/en/jsr/detail?id=82. Cette JSR a été créé en 2000 et sa dernière mise à jour est de 2010.

En utilisant cette api les développeurs n'ont pas à se préocuper de la façon dont l'os et les matériels gère bluetooth, leur code est (théoriquement) immédiatement portable pourvu qu'au classpath soit ajouté une implémentation du stack pour l'environnement cible.

Sur la base de cette JSR plusieurs éditeurs ont proposé leurs implémentations dont certaines en licence open-source comme bluecove. Tous les éditeurs n'ont pas forcément choisi de supporter tous les os comme electric blue qui ne supporte que les stacks windows.



Un mot sur les emulateurs

Pour faire tourner les tests unitaires, pour développer sur des machines ne disposant pas de stack bluetooth, on peut remplacer le stack natif par un émulateur. Pour blue cove on peut utiliser http://bluecove.org/bluecove-emu/. Electric blue fournit aussi un émulateur : http://www.javabluetoothstack.com/bluesim.htm.


Commençons par découvrir les devices bluetooth autour de nous

Je vais utiliser bluecove pour la partie pc aussi bien sur debian que sur windows

Le code complet du projet eclipse illustrant ce tutoriel peut-être téléchargé ici, toutes les librairies sont incluses.
https://www.dropbox.com/s/e5r74khzt75wvbq/bluetooth.zip

Installation sous Debian x64

Le support pour x64 de blue cove est disponible depuis la version 2.1.1 vous trouverez les derniers snapshots ici.
http://snapshot.bluecove.org/distribution/download/
Le projet eclipse les inclut également vous n'avez pas besoin de les télécharger.

Ensuite il faudra vous assurer que le package libluetooth-dev est installé sur la debian
# apt-cache show libbluetooth-dev

Sinon vous devrez l'installer
# apt-get install libluetooth-dev

Par défaut bluecove ira chercher cette librairie dans /usr/lib/ créer donc un lien sybolique vers cette librairie dans ce répertoire

# ln -s /usr/lib/x86_64-linux-gnu/libbluetooth.so /usr/lib/libbluetooth.so


Installation sous Windows

Windows 7 et Windows 8 on été testé avec une version récente du JDK sur un ordinateur portable disposant du bluetooth et ont fonctionné sans efforts particuliers.

Attention néanmoins pour les tests sous windows 8 j'ai utilisé le portable de ma femme et j'ai acheté un dispositif bluetooth USB car son HP n'avait pas de bluetooth intégré. Dans l'emballage de l'USB était proposé par défaut les drivers de BlueSoleil qui sont très mal supportés par BlueCove. J'ai du les désinstaller puis replugger le dongle et laisser Windows 8 installer son propre stack : Winsock. Pour une listes des stacks supporté par blueCove : https://code.google.com/p/bluecove/wiki/stacks.

 Lancer l'application

Maintenant pour tester que le java stack bluetooth est opérationel faite run as java application sur la classe Client.



Vous pouvez constater que mon Samsung Galaxy S2 a été découvert. Pour ce faire il faut rendre le téléphone découvrable le temps que vous exécutiez ce test : en activant le bluetooth android vous proposera de le rendre découvrable pendant deux minutes.


On remarque aussi que dans l'environnement linux c'est le stack bluez qui est utilisé, dans un environnement windows c'est le stack winsock qui sera utilisé.



Cette première étape est necessaire c'est l'étape d'inquiry: découvrir les autres dispositifs bluetooth. Après avoir découvert les dispositifs il faut maintenant se connecter à ces derniers.


Liens utile :

La documentation offcielle d'android sur l'utilisation de bluetooth
http://developer.android.com/guide/topics/connectivity/bluetooth.html

Un tutoriel sur l'api bluetooth d'android.
http://homepages.ius.edu/RWISMAN/C490/html/Android-Bluetooth.htm


Un tutoriel sur l'utilisation de la JSR 82
http://homepages.ius.edu/RWISMAN/C490/html/JavaandBluetooth.htm

Sur le site d'Oracle : Putting the core API to work
http://www.oracle.com/technetwork/articles/javame/index-140411.html








samedi 7 septembre 2013

Petit pense bête de commande linux

Voici un petit florilège de commande vraiment pratique lorsque l'on se retrouve en ligne de commande que j'ai repris du wiki de lutece.

permet de chercher dans l'historique de la ligne de commande. (Esc pour en sortir)

"Ctrl + r"

On peut avoir toutes les lignes avec

history | grep 'marecherche'


Tuer tous les processus Java :
killall java
 
Quels processus écoutent sur le port 8080 :
lsof -i :8080
 
Quels processus sont associés à tomcat :
ps aux | grep -i tomcat
 
Supprimer le processus 21456 :
kill -9 21456
 
Suivre ce qui se passe dans le fichier de log "catalina.out" :
tail -f logs/catalina.out
 
Programmer la sauvegarde automatique de la base Mysql dans le cron linux (contab -e)
* */2 * * * mysqldump -uroot -pmotdepasse mabase -rdump`date +%Y%m%d`.sql > sauvegarde.log

Suivre l'utilisation du serveur par les processus (les touches z et c permettent de changer le mode de visualisation) :
top
 
Autres :

pgrep -u$LOGINNAME mysqld
strace -p20687 -f -c -q

Commandes Linux utiles

Utilisateurs

Ajouter un utilisateur
$ sudo useradd -m nom_utilisateur -s /bin/bash
 
Ajouter un utilisateur sudoer
$ sudo useradd -m nom_utilisateur -s /bin/bash -G admin
 
Supprimer un utilisateur
$ sudo userdel nom_utilisateur

Puis supprimer le dossier de l'utilisateur qui est probablement dans /home
$ rm -rf /home/nom_utilisateur

Gestion des fichiers

Convertir une arborescence de noms de fichiers d'un encoding à un autre :
convmv -f iso-8859-15 -t UTF-8 -r --nosmart --notest .
 
Convertir une arborescence de contenus de fichiers d'un encoding à un autre :
$ find . -name *.html -type f | (while read file; do iconv -f UTF-8 -t ISO-8859-15 "$file" > "${file%.html}.htmlnew"; done)
$ find . -name "*.htmlnew" -type f | (while read file; do mv $file `echo $file | sed 's/\(.*\.\)htmlnew/\1html/'` ; done

Synchroniser un dossier avec rsync
$ rsync -avz -e ssh username@xxx.xxx.xxx.xxx:/home/username/folder/backup_srcp/mountmd2/srcp_prod/* .
 Cette commande synchronise le dossier courant avec le dossier distant /home/username/folder/backup_srcp/mountmd2/srcp_prod/ sur la machine xxx.xxx.xxx.xxx

Faire une recherche récursive d'un texte sur l'ensemble des fichiers du répertoire

$ grep -Ri le_texte_a_rechercher * 

Remplacer une chaîne de caractères récursivement dans un dossier :

$ find /some/dir -type f | xargs sed -i 's|chaine_a_trouver|chaine_a_remplacer|' 

Avec du REGEX :
$ grep -rlE "^regexhere$" /some/dir | xargs sed -i 's|string1|string2|'

Infos mémoire et espace disque

infos io
iostat > iostat.txt
 
infos memoire
$ cat /proc/meminfo > meminfo.txt
 $ vmstat > vmstat.txt
usage disque
$ df -h > df.txt
usage disque par dossier :
$ du -h
usage disque du dossier courant : du -sh * * vitesse du disque - pour celle-ci, remplacer sdxX par les disques réellement utilisés :
$ hdparm -tT /dev/sdxX > hd_sdxX.txt
activités systèmes
$ sar -A > sar.txt
info matériel
dmidecode > dmidecode.txt
Vérifier les ports d'écoutes :
$ netstat -l

Commandes VIM

Aller à la fin d'un fichier avec Vim :
MAJ + G
Aller au début d'un fichier avec Vim :
1 puis G
Aller à la ligne XXX d'un fichier avec Vim :
XXX puis G

MySQL

Lister les tables d'une base MySql :
mysql -uroot -pmotdepasse -Dlutece -e"show tables"
Sauvegarder une base de donnée du nom de "lutece" dans "dump.sql" :
mysqldump -uroot -pmotdepasse lutece -rdump.sql
Jouer le script "dump.sql" dans la base "lutece" :
mysql -uroot -pmotdepasse -Dlutece -e"source dump.sql"

Apache sur Ubuntu


Voir la liste des modules disponibles d'Apache :
$ ll /etc/apache2/mods-available

Ajouter un nouveau module Apache :
$ sudo a2enmod
$ sudo service apache2 restart 

Retirer un module Apache :
$ sudo a2dismod
$ sudo service apache2 restart 


Ajouter un site :
$ sudo a2ensite
$ sudo service apache2 restart

samedi 24 août 2013

Installer virtual box sur la debian 7.1 Wheezy

Je rédige ce petit billet car j'ai eu quelques difficultés à installer virtualbox sur ma debian wheezy.

En passant par le gestionnaire de paquets j'ai récupéré ces messages d'erreurs au moment où j'ai voulu démarrer une VM XP

Virtualbox, Kernel driver not installed (rc=-1908) 

Après quelques lectures il s'avère necessaire d'installer virtualbox-ose-guest-dkms qui necessite lui-même les headers de linux.

Un lien pour mieux comprendre le rôle des DKMS.

On commence donc par installer les headers

 apt-get install linux-headers-$(uname -r)

Puis ensuite installer virtualbox en ajoutant le package virtualbox-ose-guest-dkms


apt-get install virtualbox virtualbox-dkms virtualbox-ose-guest-dkms virtualbox-guest-utils virtualbox-guest-additions virtualbox-qt

Ensuite plus de soucis virtual box fonctionne bien avec XP.

jeudi 22 août 2013

Learn multiplication tables with Space invader !


Type the alien value and press enter to kill it. Level more options below
Sorry, your browser is not supported. Update your browser : IE 9 or greater, Any versions of Chrome, Firefox 2 or greater, Opera 9 or greater. Updtating your browser improve also the security
2 3 4 5 6 7 8 9 10

lundi 19 août 2013

What's the role of save and restore in canvas context

When I started to work with canvas I was wondering the role of save and restore in canvas.

 Save store the drawing state of the context, do interesting things with a new drawing state and restore it when you have finished.

 Let's take an example to understand it. Imagine you want to build an ellipse and a circle. For the circle no problem :


But there's no function for the ellipse except if you decide to change the scale of the drawing context.


 The drawing state of the canvas has been changed with ctx.scale(1,0.6) which flat the coordinate system on the y axis, creating an ellipse.

So why not creating a circle and an ellipse this way :


As you can see I made an ellipse and a circle but they don't have the same center because when I scale vertically the drawing state it reduced the coordinate of the y center. Beside I kind of restore the drawing state with this code ctx.scale(1, 1/0.6); which is error prone because you may forget a change you didn't undo.

Here is a solution with translate and save/restore


 Now we have an ellipse and a circle with the same center.






dimanche 7 juillet 2013

Comment fonctionne les transactions bitcoins pour les nuls.

Comprendre comment fonctionne le système de transactions des bitcoins n'est pas simple, on trouve bien ce schéma sur zerohedge qui a le mérite de faire une bonne synthèse mais ne s'adresse pas exactement à des "nuls". Je me propose donc de le faire ici, car je suis aussi un "nul".

Lorsque je parle de bitcoins avec mes amis l'idée d'une monnaie virtuelle ne plait pas beaucoup, parce que c'est virtuelle justement. J'ai souvent pas trop de mal à contrecarrer cet argument en expliquant que finalement une monnaie papier est tout aussi virtuelle, c'est une norme imposée par une banque centrale et qu'à tout moment on risque une très grosse inflation, si cette banque a envie de faire marcher son imprimerie.



Prenons l'or ou l'argent, bitcoins est beaucoup plus proche de ces valeurs d'échanges que ne pourrait l'être l'euro ou le dollar. Aucune banque ne peut émettre artificiellement de l'or. Si on veut mettre plus d'or sur le marché il faut le sortir des mines et de toute façon les ressources sont physiquement limités par les réserves naturelles de la planète, plus ça va plus obtenir de l'or devient dur.

Et bien bitcoins suit exactement cette règle. Il s'agit de séquences numériques obéissant à des propriétés mathématiques particulières qui font qu'elles sont  limitées en nombre (21 millions de séquences possibles) et plus ça va plus c'est difficile à trouver.


A ce stade vous me direz qu'à la différence de l'or le bitcoin n'est qu'une séquence numérique, je n'ai qu'à faire un copier coller. Avec de l'or ou de l'argent (ou avec sa copine) on ne peut pas faire de copier coller. Comment est-ce qu'on garantit que quelqu'un n'envoie pas deux fois le même bitcoins à des personnes différentes ?



Et bien c'est ici qu'intervient ce que l'on appelle le journal des transactions, à chaque fois que vous versez un bitcoins à quelqu'un vous le déclarez à tout le réseau et tous les nœuds l'enregistrent dans leur journal de transaction. Finalement le bénéficiaire du bitcoin recevra des confirmations des autres membres du réseau lui signifiant que cette transaction est valide.

Si par exemple le noeud 1 après avoir envoyé des bitcoins au noeud 2 tente d'envoyer maintenant les mêmes bitcoins au noeud 3 il pourra le faire mais cette transaction ne sera pas validée par les autres noeuds qui dans leur journal de transaction auront enregistré que les bitcoins sont bien en possessions du noeud 2 et ne peuvent donc plus être transférés depuis le noeud 1.

En fait si le noeud 3 a un journal de transaction à jour il pourra lui-même invalider ce transfert car il pourra y lire que le noeud 1 a déjà donné ces bitcoins au noeud 2.



Oui mais là vous pourriez m'opposer cet argument : qui détient la vérité ? Comme nous sommes sur un système décentralisé chacun maintient le journal des transaction. N'oublions pas que c'est le journal de toutes les transactions ! Tout les échanges qui ont pu avoir lieu depuis la création du bitcoin sont enregistrés dans ce journal.  Donc ça pèse assez lourd sur un PC ...


Dans cette capture vous constatez que mon journal fait bientôt 8 Go ...

Et puis encore une autre question : si le logiciel est hors ligne pendant trois mois, comment remet-il à jour son journal quand il revient en ligne.


Ici mon client bitcoins remet son journal des transactions à jour, il a 11 semaines de retards.

Et bien la réponse à ces deux questions (qui détient la vérité et comment un journal de transactions est-il mis à jour) est la même : c'est celui qui à la plus longue qui a raison !

Non non, ça n'est pas ce que vous croyez, quand je parle de la plus longue, je parle évidemment de la longueur du journal des transactions. Ce journal de transaction est plus connus sous le nom de chaîne de blocks. Donc celui qui a raison est celui qui a la chaîne de blocks la plus longue. Et quand un client doit se remettre à jour il recopie la chaîne de blocks la plus longue disponible sur le réseau.

A ce stade on peut se poser deux questions : pourquoi des chaines de blocks, et pourquoi la plus longue est-elle la gagnante ?



On parle de chaine blocks parce que les transactions ne sont pas ajoutées une à une au journal des transactions mais elles sont regroupées en blocks et sont ensuite intégrées au journal de transaction.




Mais ce processus d'intégration du block est un processus qui exige de la puissance processeurs et surtout qui peut être fait collectivement. Ainsi 10 processeurs répartis sur le réseau travaillant collectivement à l'intégration d'un block ira plus vite que 5 processeurs ayant les mêmes caractéristiques. Ce qui nous amène à ceci : s'il y a plus de gens honnêtes qui travaillent pour intégrer des blocks que de gens malhonnêtes alors la chaîne de blocks qui détient la vérité sera la plus longue ...

Pour ne pas aider ceux qui serait tenter de "réécrire l'histoire des transactions", l'intégration d'un block dépend toujours du block précédent.

Supposons que je sois un pirate qui décide de réécrire l'histoire des transactions et que je décide de changer une transaction située dans le bloc b3.


Je ne devrai pas seulement refaire l'intégration de b3 à la chaine mais aussi de b4, b5 et b6, ce qui va exiger du temps et de la ressource. Pendant ce temps les nœuds honnêtes continue d'ajouter des blocks, ils sont donc toujours en mesure de présenter une chaîne plus longue que celle je tente péniblement de réécrire.



En fait on montre que la probabilité qu'un pirate rattrape les blocks diminue exponentiellement a chaque bloc de retard. Et pendant que le bénéficiaire attend les validations de nouveaux blocks sont ajoutés.



Mais alors comment sont rémunérés ceux qui valident les blocks. Et bien vous pouvez accélérer les validations en intégrant des frais de transactions, le nœud qui aura intégré votre transaction dans la chaîne recevra ces frais.

Pour conclure on a un système qui est garanti par une information faisant autorité sans que cette information soit garantie par une autorité. C'est quelque chose de complètement nouveau qui n'a pas de précédent dans l'histoire.



mercredi 3 juillet 2013

Transformation vs Job in kettle

When I started to learn kettle I bump into two keywords : Job and Tranformation. And I was unable discriminate bettwen the two.

 Transformation is about managing a flow of column. By managing I mean getting it from an output like a csv file, adding new columns, transforming their values or assigning values, doing cartesian product, removing etc to finally send them to an output like a table in a database.

 Here is an example in the Spoon IDE of a transformation that load a dimension table:
A transformation is made of steps (max_dim_staff_last_update, Staff, Select values ...) that alterate the flow of columns.

Job manage the transformations. A job is made of  transformations. The job define in wich order the transformation are going to be called. For instance you first need to update all the dimensions tables before updating the facts table in an olap cube. Here is a Job in the Spoon IDE, you can notice that all the dims are loaded before the facts :



Another difference is about the flow of data. In a transformation when a line is done in a step, it's passed to the next step and doen't wait that all the lines are done. But in a Job the data is passed from a transformation to the next when all the lines in the previous transformations are treated.

Actually, often no data are passed from a transformation to another. For instance nothing is passed from load_dim_staff to load_dim_customer.

mercredi 12 juin 2013

Créer un pool de connexion Olap avec Olap4J et DBCP

Olap4J est un projet qui a (entre autre) pour but d'exposer les connexions et les requètes Olap comme des requêtes JDBC. Cela présente beaucoup d'avantage comme par exemple le fait de se retrouver avec des api que l'on connait déjà bien ou de pouvoir utiliser les efforts déjà fait sur les pool de connexion comme C3P0 ou common DBCP.

J'ai rencontré quelques difficultés en utilisant DBCP 1.2.1, et vous devrez pour des raisons de "legacy" souvent travailler avec cette version de la librairie. A la suite je présente le code que l'on pourrait utiliser avec dbcp 1.4


Getting the number of weeks in a year

mardi 4 juin 2013

Why using memcached instead of a local cache solution

I decide to have a look to memcached and see what  it is all about. I found this small self-explenatory story in their wiki. I finally understood that memcached let you use the ram of other servers in your network. Actually you query this distributed cache through TCP-IP



Memcached has an algorithm that choose the server based on the key input.

But it supposes you buy new servers, connect them to your network. And if you need to increase your cache to, let's say 1 Gb, you need to buy a new server ! This caching solution look expensive to me and do not compete with a local cache. It is true that accessing data in the RAM is much faster than accessing data on a local storage, but there is the overhead of the TCP connections.. ...

On his blog  Artur Ejmond compares APC (Which is a local cache solution) and memcached. The result is clear APC is much faster and much cheaper.


Then why using memcached instead of a local cache solution ?

I can see two reasons  :

  1. You have more than one client for the cache and you want all of them to share the same cache
  2. Theres's a lot of write on the cache and your disk risk to bottleneck
I know that many major sites like wikipedia or twitter use memcached, but I would be happy to hear why you choose memcached over a local cache solution. 


vendredi 24 mai 2013

Algobox Challenge

Pour tous ceux qui connaissent Algobox j'ai un petit défi à leur proposer : Dessiner un flocon de Von Koch avec AlgoBox.

Pour être plus précis en fonction du nombre d'itération n défini par l'utilisateur on réalise le flocon.

File:Quadratic Koch curve type1 iterations.png

C'est un défi car en général ces problèmes sont faciles a résoudre avec la récursivité, mais Algobox ne permet pas de faire de la récursivité.

Dans mon cas j'ai un peu dégénéré le flocon en faisant le carré central plus court pour mieux voir les contours.

n = 2
 n = 3
 n = 4
 n = 5

Cette exercice est le plus avancé que je propose sur Algobox. Les heureux challengers se verront cités sur twitter et je confirmerai leur compétence sur Viadeo en Algorithmique (super prix ;-).

Bon courage.