JXTA


Einleitung

Begriffe und Konzepte

Peers

wie in anderen P2P Systemen, die elementaren (Software) Einheiten, oft identisch mit Geräten im Netzwerk

Peer Groups

Peers müssen in Peer-Gruppen enthalten sein. Ein Peer kann in mehreren Gruppen gleichzeitig sein. Ein Peer ist immer in der World Peer Group und in der Net Peer Group. Alle Services beziehen sich nur auf die aktive Gruppe.

Die Gruppen bieten einen Namensraum und eine Sicherheitsumgebung für den Zugang und die Kommunikation.

Pipes

elementare Kommunikationskanäle, vergleichbar zu (UDP) Sockets.
Implementiert z.B. mit TCP/IP oder HTTP, in der einfachsten Form asynchron und unzuverlässig.

Advertisements

allgemeiner Publikationsmechanismus. XML Dokumente, die alle JXTA Resourcen (Peers, Peer Groups, Pipes, etc.) bekannt machen. Advertisements werden meist lokal gecached.

Discovery

Basismechanismus zum Finden von Advertisements, also zum Finden aller JXTA Resourcen.

Modules

Abstraktion für Programmcode. z.B. Java Class oder Jar, DLL, SO. Ermöglicht es einem Peer neue Dienste zu erzeugen, in dem er Code sucht, findet, läd und ausführt.

Network Services

Dienste, die von einem Peer oder einer Gruppe von Peers (evtl. kooperativ) angeboten werden.

Messages

elementare Einheit der kommunizierten Nachrichten. Können im XML- oder Binärformat vorliegen. Eine Nachricht ist eine geordnete Folge von Nachrichtenelementen, die einen Typ und einen Namen haben.

Security

Die JXTA XML Nachrichten können Informationen über Zertifikate, Credentials, Digests, Public Keys etc. enthalten. Die Nachrichtenelemente können verschlüsselt und / oder signiert sein. Jeder JXTA Peer besitzt einen Public und Private Key.

IDs

Eindeutiger Kennzeichner einer JXTA Resource (Peer, Peer Group, Pipes, Services, etc.). IDs sind wie URNs aufgebaut: urn:jxta:uuid-2233...000203


Java Anwendungen

Hello World mit Java JXTA

  1. zum Compilieren und Ausführen werden folgende Jar-Dateien benötigt (z.B. aus /opt/JXTA_Demo/lib):

     jxta.jar
     log4j.jar
     jxtasecurity.jar
     jxtaptls.jar
     minimalBC.jar
     beepcore.jar
     cryptix-asn1.jar
     cryptix32.jar
    
  2. PeerGroupFactory.newNetPeerGroup() liefert ein Objekt, das die NetPeer Gruppe repräsentiert und initialisiert das JXTA Laufzeitsystem

  3. über diese PeerGroup erfolgen alle weiteren Kontakte mit der JXTA Umgebung

  4. getPeerGroupID() zeigt die ID der (Net) Peer Group

  5. getPeerID() liefert die eigene ID des Peers

  6. getPeerName() zeigt den eigenen Namen

  7. nach der Initialisierung der JXTA Umgebung ist der Peer dann in der Net Peer Group sichtbar

  8. getPeerGroupAdvertisement() liefert das Advertisement der Net Peer Gruppe, das anschliessend ausgegeben wird

import net.jxta.peergroup.PeerGroup;
import net.jxta.peergroup.PeerGroupID;
import net.jxta.peergroup.PeerGroupFactory;

import net.jxta.peer.PeerID;
import net.jxta.protocol.PeerGroupAdvertisement;
import net.jxta.exception.PeerGroupException;

import net.jxta.discovery.DiscoveryService;
import net.jxta.pipe.PipeService;
import net.jxta.membership.MembershipService;
import net.jxta.resolver.ResolverService;

import net.jxta.document.StructuredTextDocument;
import net.jxta.document.MimeMediaType;

import java.io.IOException;

public class HelloWorldJXTA {

    PeerGroup gruppe = null;
    Screen sc = new Screen();

    public static void main(String[] args) {
        HelloWorldJXTA hello = new HelloWorldJXTA();
        hello.startJXTA();
    }

    void startJXTA() {
        sc.println("starting JXTA hello world");
        try {
            gruppe = PeerGroupFactory.newNetPeerGroup();
        } catch (PeerGroupException e) {
            e.printStackTrace();
            return;
        }
        if ( gruppe == null ) { 
           sc.println("Net Group not found, no way to continue");
           return;
        }

        sc.println("Net Group is: " + gruppe);
        PeerGroupID pgid = gruppe.getPeerGroupID();
        sc.println("pgid:  " + pgid);
        PeerID pid = gruppe.getPeerID();
        sc.println("pid:   " + pid);
        String pname = gruppe.getPeerName();
        sc.println("pname: " + pname);

        PeerGroupAdvertisement pgadv = gruppe.getPeerGroupAdvertisement();
        sc.println("pgadv: ");
        StructuredTextDocument doc = (StructuredTextDocument)
               pgadv.getDocument( new MimeMediaType("text/xml") );
        try {
             doc.sendToWriter( sc );
        } catch (IOException e) {
             e.printStackTrace();
        } finally {
             sc.flush();
        }

        // core services:
        DiscoveryService disco   = gruppe.getDiscoveryService();
        PipeService pipe         = gruppe.getPipeService();
        MembershipService member = gruppe.getMembershipService();
        ResolverService resolv   = gruppe.getResolverService();
        sc.println("got core services");
    }
}

Die Ausgabe des Programms zeigt der Reihe nach die Peer Group ID, die Peer ID und den Peer Namen:

starting JXTA hello world
Net Group is: net.jxta.impl.peergroup.PeerGroupInterface@118958e
pgid:  urn:jxta:jxta-NetGroup
pid:   urn:jxta:uuid-59616261646162614A78746150325033499E205EAB4C4933BF588B24A8CA76C703
pname: intec peer

Die weitere Ausgabe zeigt dann das XML Dokument, das das Advertisement der Net Peer Group enthält:

pgadv: 
<?xml version="1.0"?>

<!DOCTYPE jxta:PGA>

<jxta:PGA xmlns:jxta="http://jxta.org">
  <GID>
      urn:jxta:jxta-NetGroup
  </GID>
  <MSID>
      urn:jxta:uuid-DEADBEEFDEAFBABAFEEDBABE000000010206
  </MSID>
  <Name>
      NetPeerGroup
  </Name>
  <Desc>
      NetPeerGroup by default
  </Desc>
</jxta:PGA>

Der Rest des Programms zeigt noch den Zugriff auf verschiedene Services, die von der Gruppe angeboten werden.

JXTA Konfigurator

Beim ersten Ausführen des obigen (oder eines beliebigen) JXTA Programms wird ein Konfigurationszyklus durchlaufen. Dabei werden die Netzwerkeinstellungen, Namen und Passwörter abgefragt.

Basic JXTA Konfiguration
JXTA Konfigurator

Wird das Programm erneut ausgeführt, werden nur noch der Namen und das Passwort für den privaten Schlüssel abgefragt.

JXTA login
Name und Passwort für den privaten Schlüssel

Konfigurationsdateien

Das Verzeichnis .jxta enthalt folgende Teile:

JXTA Shell

Für die ersten Schritte mit JXTA existiert eine (Unix ähnliche) Shell. Diese Shell bietet für alle wichtigen JXTA Funktionen kleine Kommandos an. Damit lässt sich z.B. ein Überblick über die Peer Gruppen, Peers, Services etc gewinnen.

JXTA Shell
Shell zum Testen von JXTA Anwendungen

Übersicht über die Shell Kommandos, wie sie vom Kommando man geliefert wird:

cat         Concatanate and display a Shell object
chpgrp      Change the current peer group
clear       Clear the shell's screen
cms         No description available for this ShellApp
env         Display environment variable
exit        Exit the Shell
exportfile  Export to an external file
get         Get data from a pipe message
grep        Search for matching patterns
groups      Discover peer groups
help        No description available for this ShellApp
history     No description available for this ShellApp
importfile  Import an external file
instjar     Installs jar-files containing additional Shell commands
join        Join a peer group
leave       Leave a peer group
man         An on-line help command that displays information about a specific Shell command
mkadv       Make an advertisement
mkmsg       Make a pipe message
mkpgrp      Create a new peer group
mkpipe      Create a pipe
more        Page through a Shell object
peerconfig  Peer Configuration
peerinfo    Get information about peers
peers       Discover peers
put         Put data into a pipe message
rdvserver   No description available for this ShellApp
rdvstatus   Display information about rendezvous
recv        Receive a message from a pipe
rshd        JXTA Shell command interpreter
search      Discover jxta advertisements
send        Send a message into a pipe
set         Set an environment variable
setenv      Set an environment variable
sftp        Send a file to another peer
share       Share an advertisement
Shell       JXTA Shell command interpreter
sql         Issue an SQL command (not implemented)
sqlshell    JXTA SQL Shell command interpreter
talk        Talk to another peer
transports  Display information about the message transports in the current group
uninstjar   Uninstalls jar-files previously installed with 'instjar'
version     No description available for this ShellApp
wc          Count the number of lines, words, and chars in an object
who         Display credential information
whoami      Display information about a peer or peergroup

Instant JXTA Anwendung

Neben der JXTA Shell gibt es eine schöne Anwendung von JXTA, die Chat-Funktionen, Gruppen-Management und File-Sharing/Search erlaubt.

Instant JXTA start
Begrüssungsbild der Instant JXTA Anwendung

Für die Konfiguration startet sich wieder der bekannte JXTA Konfigurator. Mit "Proceed Anyway" erreicht man den Hauptschirm der Anwendung.

Instant JXTA Anwendung
Hauptschirm der Instant JXTA Anwendung

In der oberen Anzeige sind alle bekannten Peer Gruppen angezeigt. Ein "joined" hinter einem Gruppennamen gib an, dass man Mitglied dieser Gruppe ist. Die aktuelle Gruppe kann durch anklicken mit der Maus gewechselt werden. Die untere Anzeige hängt von der gewählten Funktion ab.

Neue Gruppe mit Instant JXTA
Anlegen einer neuen Peer Gruppe mit Instant JXTA

Über das Menue "Group - New Group" kann eine neue Gruppe angelegt werden. Optional kann ein Passwort für den Zugang zur Gruppe festgelegt werden.

Instant JXTA Anwendung mit neuer
Hauptschirm der Instant JXTA Anwendung mit einer neuen Peer Gruppe "InTec"

Peer Groups finden und erzeugen

Peer Groups können über den Discovery Service gesucht werden. Dieser findet lokale oder entfernte Advertisements mit den Methoden getLocalAdvertisements() und getRemoteAdvertisements(). Soll eine bestimmte Peer Group verwendet werden, müssen alle Programme die richtige ID der Peer Group verwenden. Zur Erzeugung einer Peer Group dient die Methode newGroup(Advertisement) bzw. newGroup( ID, Impl, name, desc).

String pgname = "InTecPeerGroup";
String pgURL  = "jxta:uuid-3E2406ADF2E542FD87F1A9744052C30D02";

private PeerGroup searchPG(String pgname) throws Exception {
        PeerGroup ipg = null;
        DiscoveryService disco = gruppe.getDiscoveryService();
        Enumeration pgs = null;
        for (int i = RETRIES; i > 0; i-- ){
            try {
                pgs = disco.getLocalAdvertisements(
                                 DiscoveryService.GROUP,
                                 "Name",
                                 pgname);
                if ( pgs != null && pgs.hasMoreElements() ) break;
                disco.getRemoteAdvertisements(
                                 null,
                                 DiscoveryService.GROUP,
                                 "Name",
                                 pgname,
                                 1,
                                 null);
                try { 
                    Thread.sleep( TIMEOUT );
                } catch (InterruptedException e) { }
            } catch (IOException e) { }
        }

        PeerGroupAdvertisement pgAdv = null;
        if ( pgs != null && pgs.hasMoreElements() ) {
        sc.println("PeerGroup " + pgname + " found");
            try {
             pgAdv = (PeerGroupAdvertisement) pgs.nextElement();
                 ipg = gruppe.newGroup( pgAdv );
            } catch (PeerGroupException e) {
                e.printStackTrace();
            }
            return ipg;
        }

        sc.println("PeerGroup " + pgname + " not found, creating new one");
        ModuleImplAdvertisement implAdv = 
           gruppe.getAllPurposePeerGroupImplAdvertisement();

        ipg = gruppe.newGroup(
                      mkGroupID(),
                      implAdv,
                      pgname,
                      "The " + pgname + " Description"
                      );
        return ipg;
}

private PeerGroupID mkGroupID() throws Exception {
        URL u = new URL( "urn", "", pgURL );
        return (PeerGroupID) IDFactory.fromURL( u );
}

Die Ausgabe könnte z.B. so aussehen:

searching InTecPeerGroup
PeerGroup InTecPeerGroup found
searched InTecPeerGroup
ipgid:  urn:jxta:uuid-3E2406ADF2E542FD87F1A9744052C30D02

Mitglied einer Peer Group werden

Advertisements werden auch mit Hilfe des Discovery Services publiziert. Die entscheidende Methode ist remotePublish().

    PeerGroupAdvertisement adv = ipg.getPeerGroupAdvertisement();
    DiscoveryService disco = gruppe.getDiscoveryService();
    disco.remotePublish(adv, DiscoveryService.GROUP);
    sc.println(pgname + " published");

Mit Hilfe des Membership Services kann man Mitglied in einer Peer Group werden. Der Service bietet dafür die Methoden apply() und join().

private void joinPeerGroup(PeerGroup pg) throws Exception {
    MembershipService ms = pg.getMembershipService();

    AuthenticationCredential ac = 
          new AuthenticationCredential(pg, null, null);
    Authenticator a = ms.apply( ac );

    if ( a.isReadyForJoin() ) {
       ms.join( a );
    } else {
       sc.println("a not ready for join "+ pg.getPeerGroupName());
    }
}

Beispiel Ausgabe:

joining peer group InTecPeerGroup
peer group joined

Peers finden

Wie bei Peer Groups findet man Peers auch mit Hilfe des Discovery Service. Die Methoden sind wieder getLocalAdvertisements() und getRemoteAdvertisements(), wobei jetzt der Typ disco.PEER verwendet wird.

private void discoverPeers(PeerGroup pg) throws Exception {
    sc.println("discovering peers in group " + pg.getPeerGroupName() );
    DiscoveryService disco = pg.getDiscoveryService();
    Enumeration ps = null;
    for (int i = RETRIES; i > 0; i-- ){
        try {
            disco.getRemoteAdvertisements(
                   null, disco.PEER, null, null,2, null);
                try { 
                    Thread.sleep( TIMEOUT );
                } catch (InterruptedException e) { }
            } catch (/*IO*/Exception e) { }
    }
    ps = disco.getLocalAdvertisements(disco.PEER, null, null);
        // sc.println("peers: " + ps);
    if ( ps == null ) return;
    while ( ps.hasMoreElements() ) {
        try {
             PeerAdvertisement pa = 
                  (PeerAdvertisement) ps.nextElement();
             sc.println("found peer: " + pa.getName());
        } catch (Exception e) {
                e.printStackTrace();
        }
    }
}

Die Ausgabe könnte wie folgt aussehen:

discovering peers
discovering peers in group InTecPeerGroup
found peer: InTec Peer
found peer: Heinz jxta_2.1 shell
found peer: InTec java Peer
discovering peers in group NetPeerGroup
found peer: InTec Peer
found peer: Heinz jxta_2.1 shell
found peer: InTec java Peer
peers discovered

Die weiteren Möglichkeiten von JXTA, wie die Verwendung von Pipes oder der Aufbau von verschlüsselten Verbindungen können hier nicht behandelt werden.


Protokolle

JXTA Architecture Layers
Schichten der JXTA Software-Architektur
Quelle: www.jxta.org

JXTA kann mit unidirektionalen, asynchronen Netzverbindungen arbeiten und eigent sich dadurch auch für PDAs und Handys, optimal sind natürlich TCP/IP oder HTTP.

Da Peers zu jeder Zeit kommen und wieder verschwinden können, dürfen in JXTA Programmen keine Annahmen über Antwortzeiten machen, bzw. nicht voraussetzen, dass überhaupt eine Antwort zurückkommt.

Content (also Dateien, MP3s, etc.) kann nach dem Publizieren unerreichbar sein (weil der Peer oder ein Routing-Peer verschwindet). Content kann zu mehreren Peers kopiert (repliziert) werden, wo er evtl. nicht mehr gelöscht werden kann.

Software Architektur

Einordnung der JXTA Protokolle in die P2P Software Architektur

IDs

JXTA IDs kann man als virtuelle IP-Adressen (mit Port-Nummern) auffassen. IDs gibt es zur Zeit für folgende Resourcen:

Eigenschaften der IDs:

Allgemeiner Aufbau einer ID:

     urn:jxta:format-1234567890abcde...

urn: und jxta: müssen als solche Zeichenketten vorkommen.

Für format gibt es z.Z. folgende Möglichkeiten: uuid für eindeutige IDs und jxta für vordefinierte IDs.

     urn:jxta:uuid-123456789002
     urn:jxta:uuid-123456789003

     urn:jxta:jxta-WorldGroup
     urn:jxta:jxta-NetGroup
     urn:jxta:jxta-Null

Die letzten beiden Hexziffern einer uuid definieren den Typ der ID. Z.B. '02' für Peergruppen-IDs, '03' für Peer-IDs, '05' für Module-Class-IDs, '06' für Module-Service-IDs.

XML Schema Definitionen für JXTA IDs:
<xs:simpleType name="JXTAID">
 <xs:restriction base="xs:anyURI">
  <xs:pattern value="([uU][rR][nN]:[jJ][xX][tT][aA]:)+\-+"/>
 </xs:restriction>
</xs:simpleType>

Advertisements

Advertisements beschreiben JXTA Resourcen im XML-Format. Die verschiedenen Advertisements werden durch XML Schema spezifiziert.

Die Definition für ein Peer Advertisement ist wie folgt:

<xs:element name="PA" type="jxta:PA"/>

<xs:complexType name="PA">
  <xs:sequence>
   <xs:element name="PID" type="JXTAID"/>
   <xs:element name="GID" type="JXTAID"/>
   <xs:element name="Name" type="xs:string" 
               minOccurs="0"/>
   <xs:element name="Desc" type="xs:anyType" 
               minOccurs="0"/>
   <xs:element name="Svc" type="jxta:serviceParam" 
               minOccurs="0" maxOccurs="unbounded"/>
  <xs:sequence>
</xs:complexType>

<xs:complexType name="serviceParam">
  <xs:sequence>
    <xs:element name="MCID" type="jxta:JXTAID"/>
    <xs:element name="Parm" type="xs:anyType"/>
  </xs:sequence>
</xs:complexType>

Beispiel für das Advertisement eines Peers:

<?xml version="1.0"?>

<!DOCTYPE jxta:PA>

<jxta:PA xmlns:jxta="http://jxta.org">
<PID>
urn:jxta:uuid-iiiiiiiiiiiiiiiiiiiiiii03
</PID>
<GID>
urn:jxta:jxta-WorldGroup
</GID>
<Name>
intec peer
</Name>
<Svc>
 <MCID>
 urn:jxta:uuid-xxxxxxxxxxxxxxxxxxxxxxx05
 </MCID>
 <Parm>
 <Addr>
 tcp://10.1.1.254:9703/
 </Addr>
 <Addr>
 jxtatls://uuid-iiiiiiiiiiiiiiiiiiiiiii03/TlsTransport/jxta-WorldGroup
 </Addr>
 <Addr>
 jxta://uuid-iiiiiiiiiiiiiiiiiiiiiii03/
 </Addr>
 </Parm>
</Svc>
...
</jxta:PA>

Die Definition für ein Peer Group Advertisement ist wie folgt:

<xs:element name="PGA" type="jxta:PGA"/>

<xs:complexType name="PGA">
  <xs:sequence>
    <xs:element name="GID" type="jxta:JXTAID"/>
    <xs:element name="MSID" type="jxta:JXTAID"/>
    <xs:element name="Name" type="xs:string" 
        minOccurs="0"/>
    <xs:element name="Desc" type="xs:anyType" 
        minOccurs="0"/>
    <xs:element name="Svc" type="jxta:serviceParam" 
        minOccurs="0" maxOccurs="unbounded"/>
  </xs:sequence>
</xs:complexType>

Beispiel für das Advertisement einer Peergroup:

<?xml version="1.0"?>

<!DOCTYPE jxta:PGA>

<jxta:PGA xmlns:jxta="http://jxta.org">
<GID>
urn:jxta:jxta-NetGroup
</GID>
<MSID>
urn:jxta:uuid-mmmmmmmmmmmmmmmmmmmmmmm06
</MSID>
<Name>
NetPeerGroup
</Name>
<Desc>
NetPeerGroup by default
</Desc>
</jxta:PGA>

Peer Resolver Protocol (PRP)

Aufbau der Query zum Finden beliebiger Resourcen

<xs:element name="ResolverQuery" type="jxta:ResolverQuery"/>

<xs:complexType name="ResolverQuery">
  <xs:all>
    <xs:element ref="jxta:Cred" minOccurs="0"/>
    <xs:element name="SrcPeerID" type="jxta:JXTAID"/>
    <!-- This could be extended with a pattern restriction -->
    <xs:element name="HandlerName" type="xs:string"/>
    <xs:element name="QueryID" type="xs:string"/>
    <xs:element name="HC" type="xs:unsignedInt"/>
    <xs:element name="Query" type="xs:anyType"/>
  </xs:all>
</xs:complexType>

Beispiel

<?xml version="1.0"?>

<!DOCTYPE jxta:ResolverQuery>

<jxta:ResolverQuery xmlns:jxta="http://jxta.org">
    <HandlerName>
      urn:jxta:uuid-yyyyyyyyyyyyyy05
    </HandlerName>
    <QueryID>
      urn:jxta:uuid-xxxxxxxxxxxxxxx
    </QueryID>
    <HC>
        5
    </HC>
    <SrcPeerID>
        urn:jxta:uuid-zzzzzzzzzzzzzzzzz03
    </SrcPeerID>
    <Query>
    ...
    </Query>
</jxta:ResolverQuery>

Aufbau der Antwort auf eine ResolverQuery:

<xs:element name="ResolverResponse" type="ResolverResponse"/>

<xs:complexType name="ResolverResponse">
  <xs:all>
    <xs:element ref="jxta:Cred" minOccurs="0"/>
    <xs:element name="HandlerName" type="xs:string"/>
    <xs:element name="QueryID" type="xs:string"/>
    <xs:element name="Response" type="xs:anyType"/>
  </xs:all>
</xs:complexType>

Endpoint Routing

Endpoint Adressen:

http://10.1.1.11:9704/endpoint/resolver

urn:jxta:uuid-pppppppppppppppppppppppp04?pipeService

urn:jxta:jxta-NetGroup?relay/uuid-iiiiiiiiiiiiiiiii03

Route Advertisement:

<xs:element name="APA" type="jxta:APA"/>

<xs:complexType name ="jxta:APA">
  <xs:sequence>
    <xs:element name="EA" type="jxta:JXTAID" 
      minOccurs="1" maxOccurs="unbounded"/>
  </xs:sequence>
</xs:complexType>

<xs:element name="RA" type="jxta:RA"/>

<xs:complexType name ="jxta:RA">
  <xs:sequence>
    <xs:element name="DstPID" type="xs:anyURI"/>
    <xs:element ref="jxta:RA"/>
    <xs:element name="Hops" minOccurs="0">
      <xs:sequence>
        <xs:element ref="jxta:APA" 
            maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:element>
  </xs:sequence>
</xs:complexType>

Endpoint Routing Query

<xs:element name="ERQ" type="jxta:ERQ"/>

<xs:complexType name ="jxta:ERQ">
  <xs:sequence>
    <xs:element name="Dst" type="jxta:JXTAID"/>
    <xs:element name="Src">
      <xs:element ref="jxta:RA"/>
    </xs:element>
  </xs:sequence>
</xs:complexType>

Endpoint Route Response

<xs:element name="ERR" type="jxta:ERR"/>

<xs:complexType name ="jxta:ERR">
  <xs:sequence>
    <xs:element name="Dst">
      <xs:element ref="jxta:RA"/>
    </xs:element>
    <xs:element name="Src">
      <xs:element ref="jxta:RA"/>
    </xs:element>
  </xs:sequence>
</xs:complexType>

Endpoint Router Transport Protocol

Dient dem Herstellen von virtuellen Transport-Verbindungen zwischen Peers, die nicht direkt eine TCP/IP oder HTTP Verbindung aufbauen können.

weitere Bestandteile des JXTA Protokolls

Zusammenfassung und Ausblick


© Universität Mannheim, Rechenzentrum, 2002-2003.

Heinz Kredel
Last modified: Sat Jul 19 11:00:48 CEST 2003