Introduction to liburbi Java for Urbi 1.x

(book compiled from Revision exported)

Remi Humbert

Traduit de l'anglais par Antoine 'Zelig' Hue

This document is released under the Attribution-NonCommercial-NoDerivs 2.0 Creative Commons licence (http://creativecommons.org/licenses/by-nc-nd/2.0/deed.en).


Table of Contents

1. Introduction
2. Démarage
Charger la liburbijava shared library
Connexion
Envoyer des commandes URBI
Envoyer des data binaires.
Envoyer un son
3. Réception
Tags et callbacks
UMessage
UValue
UBinary
USound
UImage
Quelques exemples
4. Developper avec Eclipse
Importer le projet liburbi java
Exécuter les exemples
5. Astuces de programmation
A. Copyright

Chapter 1. Introduction

Liburbi-java est une library créée pour encapsuler une connexion URBI. Elles gère la connexion TCP avec avec le serveur URBI, et la répartition des messages qu'il envoie. La library est thread-safe et rentrant. Cette library est générée à partir de la source Liburbi-c++ avec swig.

la library consiste en deux main classes Java, liburbi.UClient et liburbi.USyncClient, et quelques fonctions utiles.

Nous supposerons le lecteur familiarisé avec la syntaxe URBI.

Chapter 2. Démarage

Charger la liburbijava shared library

Liburbi Java est une library générée à partir de la liburbi C++ avec l'outil appelé swig. Swig génère des classes wrapper en java pour toutes les classes C++ que nous voulons exporter. Il génère aussi du code JNI pour faire le lien entre java et la library C++. Avec cela en tête, vous comprendrez mieux que pour utiliser la liburbi java, vous aurez de toutes façon charger le code C++ JNI dans votre application. Nous fournissons ce code C++ dans une library partagée appelée urbijava.dll sous Windows, liburbijava.so sous Linux, et liburbijava.dylib sous MacOs. Pour charger la library dans votre code java, utilisez la fonction système .loadLibrary. Par exemple placez cet appel dans votre classe main comme ci-dessous:

// Toutes les classes liburbi se situent dans le package liburbi
import liburbi.main.UClient;
import liburbi.main.liburbi;

public class Main {

  static {
    System.loadLibrary("urbijava");
  }

NB: Le chemin vers la library que vous chargez 'urbijava' doit être dans votre chemin pour que votre programme puisse la trouver. Sous Windows assurez vous que la variable d'environnement contiens le chemin vers la library urbijava. Sous Unix assurez vous que votre LD_LIBRARY_PATH contiens le chemin de la library urbijava.

Connexion

Pour se connecter à un serveur URBI, créez simplement une nouvelle instance de liburbi.main.UClient ( ou liburbi.main.USyncClient si vous voulez utiliser les fonctions synchrones décrites plus bas), en passant le nom ou l'adresse du serveur comme premier paramètre, et optionnellement le port comme second paramètre:


  public static void main (String[] arg) {

    UClient client = new UClient("myrobot.ensta.fr");

    // un wrapper est disponible aussi dans la classe liburbi.main.liburbi:
    // UClient client = liburbi.connect("myrobot.ensta.fr");

Le constructeur debutera un thread indépendant qui sera en attente de messages venant du serveur URBI.

Vous pouvez vérifier si la connection a été établie avec succès en appellant la fonction error, qui retourne la valeur zero en cas de succès, ou un code différent d ezero en cas d'erreur.


    if (client.error() != 0)
    {
	System.err.println("Couldn't connect to the URBI server.");
	return;
    }

Envoyer des commandes URBI

La méthode send est le moyen le plus simple d'envoyer des commandes au serveur URBI. Il prends une String comme paramètre:


    int sleeptime = 50;

    client.send("motoron;");

    for (float val=0; val<=1; val+=0.05)
      client->send("neck.val = " + val + ";wait (" + sleeptime ");");

Quelques fonctions statiques sont définies dans la classe liburbi.liburbi: 'getComma ()', 'getSemicolon()', 'getPipe()' et 'getParallel()' et elles retournent les caractères ',', ';', '|' et '&' respectivement.

Envoyer des data binaires.

Pour envoyer des data binaires au robot, la méthode sendBin doit être utilisée. Elle prends comme paramètres le buffer à envoyer (nous supportons uniquement le type byte[] pour le moment) et sa taille, et en option une en-tête.

    client->sendBin(soundData, soundDataSize,
                    "speaker.val = BIN " + soundDataSize + " raw 2 16000 16 1;");

Envoyer un son

De la même façon vous pouvez utiliser sendBin pour jouer un son sur le robot, une méthode spécifique et efficace a été écrite à cet effet: sendSound.

client->sendSound("speaker", sound, "endsound");

Le premier paramètre est le périphérique son auquel envoyer nous envoyons le son, le second paramètre est une classe liburbi.main.USound décrivant le son à envoyer. Le troisième est un tag optionnel qui sera utilisé par le serveur pour émettre un message système "stop" quand le son sera fini. La fonction convert (in liburbi.liburbi class) peut être utilisée pour les conversions entre les différents formats de sons.

Il n'y a aucune limite à la taille du buffer de son, celui-ci étant automatiquement découpé en tronçons par la library. Les data sont copiés par la library: le paramètre USound et ses data associés peuvent être libérés en toute sécurité dés le retour de la fonction.

Chapter 3. Réception

Tags et callbacks

La plupart des messages reçus du serveur URBI sont le résultat d'une commande envoyée précédemment. Le fonctionnement des tags URBI autorise à lier un message à sa réponse: à chaque commande est associé un tag, et ce tag est répété dans le message de réponse. La classe liburbi.main.UClient gère la réception de ces messages dans le thread indépendant créé par le constructeur, les parse et remplis une structure UMessage.

Dans votre programme java, vous pouvez avoir des extensions de classes liburbi.main.UCallbackInterface et redéfinir sa méthode onMessage. Vous pouvez enregistrer ces classes avec la méthode UClient setCallback et les associer avec un tag. puis chaque fois qu'un message avec ce tag est envoyé au serveur, la fonction redéfinie onMessage sera appelée avec une classe liburbi.main.UMessage comme paramètre.

La classe liburbi.main.UCallbackInterface est définie ainsi:

package liburbi.main;

public class UCallbackInterface {
  public UCallbackAction onMessage(UMessage msg);
}

La fonction onMessage prends comme paramètre un liburbi.main.UMessage et retourne un enum: liburbi.main.UCallbackAction qui peut avoir deux valeurs: liburbi.main.UCallbackAction.URBI_CONTINUE ou liburbi.main.UCallbackAction.URBI_REMOVE (dans un tel cas le retour est enlevé, et ne sera plus appelé, même si vous recevez un message avec le même tag).

La fonction setCallback est définie ainsi:

package liburbi.main;

public class UClient {

  [...]
  public long setCallback(UCallbackInterface ref, String tag);
}

donnez comme premier paramètre votre classe redéfinie liburbi.main.UCallbackInterface, et donnez comme second argument une String correspondant au tag auquel vous voulez associer le retour.

UMessage

La structure UMessage peut stocker les informations contenues dans toutes sortes de messages URBI en utilisant une fonction "getType" et une UValue (union de classes). Ces deux classes sont définies ainsi:


package liburbi.main;

public enum UMessageType {
  MESSAGE_SYSTEM, /// Message système
  MESSAGE_ERROR,  /// Message d'erreur
  MESSAGE_DATA;   /// Message contenant des data dans la UValue
}

public class UMessage {

  /// Server-side timestamp.
  public int getTimestamp();

  /// Tag associé.
  public String getTag();

  /// Type de la UValue contenue dans le UMessage
  public UMessageType getType();

  /// UValue contenue dans le UMessage
  public UValue getValue();

  public String getMessage();

  /// Raw message sans les data binaires.
  public String getRawMessage();

  /// Client d'où provient le message.
  public UAbstractClient getClient();
}

le champ type UMessageType peut être MESSAGE_SYSTEM, MESSAGE_ERROR ou MESSAGE_DATA. Si le type est MESSAGE_DATA, le message contiens une UValue.

UValue

package liburbi.main;

public enum UDataType {
  DATA_DOUBLE,
  DATA_STRING,
  DATA_BINARY,
  DATA_LIST,
  DATA_OBJECT,
  DATA_VOID;
}

public class UValue {

  public UDataType getType();

  /// valeur si de type UDataType.DATA_STRING
  /// vérifiez que cette chaîne n'est pas null (c'est possible du côté C++,
  /// souvenez vous que liburbijava est batie au-dessus de liburbic++)
  /// avec 'isStringNull ()'
  public String getString();
  public boolean isStringNull();

  /// valeur si de type UDataType.DATA_DOUBLE
  public double getDouble();

  /// valeur si de type UDataType.DATA_BINARY
  public UBinary getUBinary();

  /// valeur si de type UDataType.DATA_LIST
  public UList getUList();

  /// utilisé pour afficher correctement la UValue
  public String toString();
}

La UValue contiens un UDataType qui peut prendre les valeurs: DATA_DOUBLE, DATA_STRING, DATA_BINARY, DATA_LIST, DATA_OBJECT, DATA_VOID. Dependant de ce champ, la valeur correspondante dans la UValue sera réglée. Si la UValue est de type binaire, elle contient une classe UBinary définie ci-après. Le UBinaryType dans la structure UBinary donnera des informations additionnelles sur le type des data (BINARY_NONE, BINARY_UNKNOWN, BINARY_IMAGE, BINARY_SOUND), et la structure de son ou d'image adaptée sera complétée.

UBinary


package liburbi.main;

public enum UBinaryType {
  BINARY_NONE,
  BINARY_UNKNOWN,
  BINARY_IMAGE,
  BINARY_SOUND;
}

public class UBinary {

  public UBinaryType getType();

  /// valeur si de type BINARY_IMAGE
  public UImage getUImage();

  /// valeur si de type BINARY_SOUND
  public USound getUSound();

  /// Taille des data
  public int getSize();

  public String getMessage();
  public String getExtraHeader();
}

USound

package liburbi.main;

public enum USoundFormat {
  SOUND_RAW,
  SOUND_WAV,
  SOUND_MP3,
  SOUND_OGG,
  SOUND_UNKNOWN;
}

public enum USoundSampleFormat {
  SAMPLE_SIGNED(1),
  SAMPLE_UNSIGNED(2);
}

public class USound {

  /// taille total en byte
  public long getSize();

  /// Nombres de canaux audio
  public int getChannels();

  /// rate en Hertz
  public int getRate();

  /// taille du sample en bit
  public int getSampleSize();

  /// format des sound data
  /// (SOUND_RAW, SOUND_WAV, SOUND_MP3...)
  public USoundFormat getSoundFormat();

  /// format du sample
  public USoundSampleFormat getSampleFormat();

  /// data du sound dans une table de byte
  public byte[] getDataAsByte();
}

UImage



package liburbi.main;

public enum UImageFormat {
  IMAGE_RGB(1),
  IMAGE_YCbCr(2),
  IMAGE_JPEG(3),
  IMAGE_PPM(4),
  IMAGE_UNKNOWN;
}

public class UImage {

  /// taille de l'image en byte
  public long getSize();

  /// largeur d'image
  public long getWidth();

  /// hauteur d'image
  public long getHeight();

  /// IMAGE_RGB, IMAGE_YCbCr, IMAGE_JPEG...
  public UImageFormat getImageFormat();

  /// data de l'image data dans une table de byte
  public byte[] getDataAsByte();
}

Quelques exemples


import liburbi.main.*;

/// Callback pour des images
class onImage extends UCallbackInterface {

  public UCallbackAction onMessage(UMessage msg) {
    if (!(UMessageType.MESSAGE_DATA == msg.getType ()))
      return UCallbackAction.URBI_CONTINUE;
    UValue value = msg.getValue ();

    if (!(UDataType.DATA_BINARY == value.getType ()))
      return UCallbackAction.URBI_CONTINUE;
    UBinary bin = value.getUBinary ();

    if (!(UBinaryType.BINARY_IMAGE == bin.getType ()))
      return UCallbackAction.URBI_CONTINUE;
    UImage img = bin.getUImage ();
    byte[] data = img.getDataAsByte ();

    System.out.println("Image of size " + img.getWidth () + "x"
                       + img.height + " received");

    /// myDisplay*Image sont des fonctions customisées qui ne font pas parties de liburbi-java
    /// replacez par votre propre code
    if (img.imageFormat == UImageFormat.IMAGE_JPEG)
      myDisplayJPEGImage (data, img.getWidth (), img.getHeight ());
    else if (img.imageFormat == UImageFormat.IMAGE_YCbCr)
      myDisplayYCbCrImage (data, img.getWidth (), img.getHeight ());
    else if (img.imageFormat == UImageFormat.IMAGE_RGB)
      myDisplayRGBImage (data, img.getWidth (), img.getHeight ());

    return UCallbackAction.URBI_CONTINUE;
  }
}

/// Callback pour un sound
class onSound extends UCallbackInterface {

  public UCallbackAction onMessage(UMessage msg) {
    if (!(UMessageType.MESSAGE_DATA == msg.getType ()))
      return UCallbackAction.URBI_CONTINUE;
    UValue value = msg.getValue ();

    if (!(UDataType.DATA_BINARY == value.getType ()))
      return UCallbackAction.URBI_CONTINUE;
    UBinary bin = value.getUBinary ();

    if (!(UBinaryType.BINARY_SOUND == bin.getType ()))
      return UCallbackAction.URBI_CONTINUE;
    USound sound = bin.getUSound ();

    byte[] data = img.getDataAsByte ();

    System.out.println("Sound received");
    return UCallbackAction.URBI_CONTINUE;
  }
}

/// Callback pour des valeurs d'articulation
class onJoint extends UCallbackInterface {

  public UCallbackAction onMessage(UMessage msg) {
    if (!(UMessageType.MESSAGE_DATA == msg.getType ()))
      return UCallbackAction.URBI_CONTINUE;
    UValue value = msg.getValue ();

    if (!(UDataType.DATA_DOUBLE == value.getType ()))
      return UCallbackAction.URBI_CONTINUE;

    System.out.println("The joint value is " + value.getDouble ());
    return UCallbackAction.URBI_CONTINUE;
  }
}

public class test {
  static {
    /// Chargez la library C++ liburbi et le code jni.
    System.loadLibrary("urbijava");
  }

  public static void main(String argv[]) {

    UClient c = new UClient(argv[1]);

    if (c.error() != 0)
    {
	System.out.println("Couldn't connect to the URBI server.");
	System.exit(1);
    }

    onImage oi = new onImage ();
    onJoint oj = new onJoint ();
    onSound os = new onSound ();

    c.setCallback (oi, "img");
    c.setCallback (oj, "snd");
    c.setCallback (os, "joint");

    c.send("img: camera.val;");
    c.send("loop snd: micro.val,");
    c.send("joint: headPan.val;");


    liburbi.execute (); /// fonction qui fait une veille infinie
  }
}

Chapter 4. Developper avec Eclipse

Importer le projet liburbi java

Nous fournissons une configuration de projet Eclipse simple que vous pouvez importer dans Eclipse et utiliser pour créer votre projet utilisant la Liburbi Java. Nous montrons ici comment faire cela:
  • Ouvrir Eclipse
  • Clic droit sur le panel Package Explorer et sélectionnez 'import' (ou allez dans File/import)
  • Selectionnez 'Existing Projects into Workspace' dans les fenêtres ouvertes
  • Clickez 'Next'
  • Entrez le chemin du sdk-remote java sur votre ordinateur
  • Eclipse trouvera le fichier .project dans ce qui est fourni et affichera le projet 'liburbi'
  • Selectionnez le projet 'liburbi' et clickez sur 'Finish'

Le projet liburbi java est chargé. Vous pouvez voir les trois packages de liburbi: liburbi.main, liburbi.image et liburbi.sound. A partir de la source dans ces trois packages, nous avons fait liburbijava.jar, que vous pouvez utiliser dans vos applications. Vous pouvez voir aussi les sources des différents exemples que nous fournissons. Nous les avons déposé dans les packages examples.nameofexample. Vous pouvez vous inspirer de ces exemples dans vos applications. Ici, nous allons voir comment les compiler et les faire tourner dans Eclipse

NB: Si Eclipse se plaint d'erreurs dans le code source, ce peut être que le niveau de compliance de votre compilateur est trop bas. Vous avez à régler le niveau de compliance du compilateur au moins sur Java 5 (Windows/Preferences/Java/Compiler).

Exécuter les exemples

Nous fournissons des fichiers sample .launch que vous pouvez charger dans Eclipse pour faire tourner les projets.
  • Clickez sur Run/Open ouvre un Dialog (ou 'Run...' dans certaines versions d'Eclipse)
  • Les configurations de lancement seront reconnues automatiquement. Vous pouvez en choisir une dans la liste, choisissons 'URBIPing'.
  • Dans le paneau de doite, sélectionnez 'Environment', dépendant de si vous êtes sous Windows, Linux ou MacOs, vous verrez le chemin variant 'path', 'LD_LIBRARY_PATH' ou 'DYLD_LIBRARY_PATH' défini.
  • editez la valeur de la variable. Réglez le chemin absolu du répertoire de liburbi java 'lib' (celui qui contient (lib)urbijava.{so,dll,dylib}). Ceci est nécessaire aux exemples pour charger la library liburbijava.
  • Puis clickez sur l'onglet 'Arguments', et entrez l'adresse du serveur URBI que vous voulez atteindre(ping) dans le champ 'program arguments'. Par exemple, si vous voulez pinger le serveur tournant dans Webots, mettez 'localhost' comme adresse (sans les quotes).
  • Clickez sur 'Apply'. Clickez sur 'Run'.

Chapter 5. Astuces de programmation

  • En dehors des cas ou ce que vous faites est simple, essayez de ne pas utiliser les fonctions sync*. Elles sont moins efficaces que les asynchrones.

  • Les fonctions avec retour devrons retourner aussi vite que possible, les retours étant tous appellé par le même thread. Si vous avez des opérations gourmandes en temps d'exécution, vous devriez les lancer dans un autre thread et utiliser des mécanismes de synchronisation comme les sémaphores ou les mutexes.

Appendix A. Copyright


THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE
TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR
"LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE
LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE
OR COPYRIGHT LAW IS PROHIBITED.

BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND
AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS
YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF
SUCH TERMS AND CONDITIONS.

1. Definitions

   1. "Collective Work" means a work, such as a periodical issue,
   anthology or encyclopedia, in which the Work in its entirety in
   unmodified form, along with a number of other contributions,
   constituting separate and independent works in themselves, are
   assembled into a collective whole. A work that constitutes a
   Collective Work will not be considered a Derivative Work (as
   defined below) for the purposes of this License.  2. "Derivative
   Work" means a work based upon the Work or upon the Work and other
   pre-existing works, such as a translation, musical arrangement,
   dramatization, fictionalization, motion picture version, sound
   recording, art reproduction, abridgment, condensation, or any other
   form in which the Work may be recast, transformed, or adapted,
   except that a work that constitutes a Collective Work will not be
   considered a Derivative Work for the purpose of this License. For
   the avoidance of doubt, where the Work is a musical composition or
   sound recording, the synchronization of the Work in timed-relation
   with a moving image ("synching") will be considered a Derivative
   Work for the purpose of this License.  3. "Licensor" means the
   individual or entity that offers the Work under the terms of this
   License.  4. "Original Author" means the individual or entity who
   created the Work.  5. "Work" means the copyrightable work of
   authorship offered under the terms of this License.  6. "You" means
   an individual or entity exercising rights under this License who
   has not previously violated the terms of this License with respect
   to the Work, or who has received express permission from the
   Licensor to exercise rights under this License despite a previous
   violation.

2. Fair Use Rights. Nothing in this license is intended to reduce,
limit, or restrict any rights arising from fair use, first sale or
other limitations on the exclusive rights of the copyright owner under
copyright law or other applicable laws.

3. License Grant. Subject to the terms and conditions of this License,
Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
perpetual (for the duration of the applicable copyright) license to
exercise the rights in the Work as stated below:

   1. to reproduce the Work, to incorporate the Work into one or more
   Collective Works, and to reproduce the Work as incorporated in the
   Collective Works; 2. to distribute copies or phonorecords of,
   display publicly, perform publicly, and perform publicly by means
   of a digital audio transmission the Work including as incorporated
   in Collective Works;

The above rights may be exercised in all media and formats whether now
known or hereafter devised. The above rights include the right to make
such modifications as are technically necessary to exercise the rights
in other media and formats, but otherwise you have no rights to make
Derivative Works. All rights not expressly granted by Licensor are
hereby reserved, including but not limited to the rights set forth in
Sections 4(d) and 4(e).

4. Restrictions.The license granted in Section 3 above is expressly
made subject to and limited by the following restrictions:

   1. You may distribute, publicly display, publicly perform, or
   publicly digitally perform the Work only under the terms of this
   License, and You must include a copy of, or the Uniform Resource
   Identifier for, this License with every copy or phonorecord of the
   Work You distribute, publicly display, publicly perform, or
   publicly digitally perform. You may not offer or impose any terms
   on the Work that alter or restrict the terms of this License or the
   recipients' exercise of the rights granted hereunder. You may not
   sublicense the Work. You must keep intact all notices that refer to
   this License and to the disclaimer of warranties. You may not
   distribute, publicly display, publicly perform, or publicly
   digitally perform the Work with any technological measures that
   control access or use of the Work in a manner inconsistent with the
   terms of this License Agreement. The above applies to the Work as
   incorporated in a Collective Work, but this does not require the
   Collective Work apart from the Work itself to be made subject to
   the terms of this License. If You create a Collective Work, upon
   notice from any Licensor You must, to the extent practicable,
   remove from the Collective Work any reference to such Licensor or
   the Original Author, as requested.  2. You may not exercise any of
   the rights granted to You in Section 3 above in any manner that is
   primarily intended for or directed toward commercial advantage or
   private monetary compensation. The exchange of the Work for other
   copyrighted works by means of digital file-sharing or otherwise
   shall not be considered to be intended for or directed toward
   commercial advantage or private monetary compensation, provided
   there is no payment of any monetary compensation in connection with
   the exchange of copyrighted works.  3. If you distribute, publicly
   display, publicly perform, or publicly digitally perform the Work,
   You must keep intact all copyright notices for the Work and give
   the Original Author credit reasonable to the medium or means You
   are utilizing by conveying the name (or pseudonym if applicable) of
   the Original Author if supplied; the title of the Work if supplied;
   and to the extent reasonably practicable, the Uniform Resource
   Identifier, if any, that Licensor specifies to be associated with
   the Work, unless such URI does not refer to the copyright notice or
   licensing information for the Work. Such credit may be implemented
   in any reasonable manner; provided, however, that in the case of a
   Collective Work, at a minimum such credit will appear where any
   other comparable authorship credit appears and in a manner at least
   as prominent as such other comparable authorship credit.  4.

      For the avoidance of doubt, where the Work is a musical
         composition: 1. Performance Royalties Under Blanket
         Licenses. Licensor reserves the exclusive right to collect,
         whether individually or via a performance rights society
         (e.g. ASCAP, BMI, SESAC), royalties for the public
         performance or public digital performance (e.g. webcast) of
         the Work if that performance is primarily intended for or
         directed toward commercial advantage or private monetary
         compensation.  2. Mechanical Rights and Statutory
         Royalties. Licensor reserves the exclusive right to collect,
         whether individually or via a music rights agency or
         designated agent (e.g. Harry Fox Agency), royalties for any
         phonorecord You create from the Work ("cover version") and
         distribute, subject to the compulsory license created by 17
         USC Section 115 of the US Copyright Act (or the equivalent in
         other jurisdictions), if Your distribution of such cover
         version is primarily intended for or directed toward
         commercial advantage or private monetary compensation.
         5. Webcasting Rights and Statutory Royalties. For the
         avoidance of doubt, where the Work is a sound recording,
         Licensor reserves the exclusive right to collect, whether
         individually or via a performance-rights society
         (e.g. SoundExchange), royalties for the public digital
         performance (e.g. webcast) of the Work, subject to the
         compulsory license created by 17 USC Section 114 of the US
         Copyright Act (or the equivalent in other jurisdictions), if
         Your public digital performance is primarily intended for or
         directed toward commercial advantage or private monetary
         compensation.

5. Representations, Warranties and Disclaimer

UNLESS OTHERWISE MUTUALLY AGREED BY THE PARTIES IN WRITING, LICENSOR
OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR
OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE,
MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR
THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF
ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO
NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY
NOT APPLY TO YOU.

6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY
APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY
LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR
EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK,
EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

7. Termination

   1. This License and the rights granted hereunder will terminate
   automatically upon any breach by You of the terms of this
   License. Individuals or entities who have received Collective Works
   from You under this License, however, will not have their licenses
   terminated provided such individuals or entities remain in full
   compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will
   survive any termination of this License.  2. Subject to the above
   terms and conditions, the license granted here is perpetual (for
   the duration of the applicable copyright in the
   Work). Notwithstanding the above, Licensor reserves the right to
   release the Work under different license terms or to stop
   distributing the Work at any time; provided, however that any such
   election will not serve to withdraw this License (or any other
   license that has been, or is required to be, granted under the
   terms of this License), and this License will continue in full
   force and effect unless terminated as stated above.

8. Miscellaneous

   1. Each time You distribute or publicly digitally perform the Work
   or a Collective Work, the Licensor offers to the recipient a
   license to the Work on the same terms and conditions as the license
   granted to You under this License.  2. If any provision of this
   License is invalid or unenforceable under applicable law, it shall
   not affect the validity or enforceability of the remainder of the
   terms of this License, and without further action by the parties to
   this agreement, such provision shall be reformed to the minimum
   extent necessary to make such provision valid and enforceable.
   3. No term or provision of this License shall be deemed waived and
   no breach consented to unless such waiver or consent shall be in
   writing and signed by the party to be charged with such waiver or
   consent.  4. This License constitutes the entire agreement between
   the parties with respect to the Work licensed here. There are no
   understandings, agreements or representations with respect to the
   Work not specified here. Licensor shall not be bound by any
   additional provisions that may appear in any communication from
   You. This License may not be modified without the mutual written
   agreement of the Licensor and You.