Getting Started with JXTA, Part 4
Pages: 1, 2
The indirection introduced by pipes also allows applications to bind to the most appropriate instance of a service: perhaps the closest service or the best performing service. JXTA presents a novel approach to address application reliability. By building and proliferating interchangeable services on peers, applications can adapt to the unreliable network environment by connecting to or changing to the most available or efficient service; the application can do this at any time and with no regard to the location of the service.
Pipes are an essential tool to build such services and applications. JXTA pipes introduce a fundamental network programming shift in which developers should not write applications that connect to a specific peer to access a unique service, but should write applications that discover the closest available service regardless of which peer is running the service.
Pipe Endpoint Binding
Pipe endpoints are dynamically bounded to a peer endpoint at runtime via the Pipe Binding Protocol (PBP). The pipe-binding process consists of searching for and connecting two or more pipe endpoints. When a message is sent over a pipe, it is sent by the local output pipe to the destination input pipe endpoint that is currently listening to the pipe. The set of currently listening peers (that is, the location of the input pipe endpoint) is resolved using the PBP.
Pipes are uniquely identified by a pipe advertisement. The pipe advertisement contains a unique pipe ID and an optional pipe name. Pipe advertisements are a resource within the JXTA network; they may be discovered just as we discovered peers and peergroups. Each pipe advertisement is associated with a unique pipe.
Applications use a pipe service to create pipe endpoints (both input and output) associated with a particular pipe advertisement. The pipe service uses pipe advertisements to identify the pipe and resolve the input pipe and output pipe endpoints.
Pipe Communication Mode
Pipes support two modes of communication (see Figure 2-5):
Point-to-point: A point-to-point pipe connects exactly two pipe endpoints: an input pipe receives messages sent from an output pipe. No reply or acknowledgment is supported. Additional information in the message payload (such as a unique ID) is required to determine the sequence of messages sent over the pipe. The message payload may also contain a pipe advertisement that can be used to open a pipe to reply to the sender.
Propagate pipe: A propagate pipe connects one output pipe to multiple input pipes. Messages flow into the input pipes from the output pipe (propagation source). A message sent over a propagate pipe is sent to all listening input pipes; this process may create multiple copies of the message. On a TCP/IP network, IP multicasting is used as an implementation for propagate pipes when the propagate scope maps to an underlying physical subnet in a one-to-one fashion. Propagate pipes can also be implemented using point-to-point communication on transports that do not support multicasting.
Pipes and Peergroups
Pipe connectivity is related to the concept of peergroups: only pipe endpoints that are located in the same peergroup can be mutually resolved by a pipe service. Each peergroup has its own pipe service, so to open a pipe connection between two peers, the two peers must have joined the same peergroup.
Of course, since all peers are part of the NetPeerGroup, any peer can open a pipe to any other peer. The difference is in which pipe service will resolve the pipe. In Figure 2-5, Peer 1 can use the pipe service of either the NetPeerGroup or of Peergroup A to resolve its pipe connections to Peers 2 and 3. Peers 2 and 4 can use only the pipe service of the NetPeerGroup to resolve their mutual pipe connection. The context of the pipe resolution is important because of the security context that may be enforced by the pipe service of a peergroup: Peer 1 may decide that it does not want to send data to any peer that has not been authenticated into Peergroup A. It then advertises only its pipe endpoint within that peergroup.
A peer can maintain different pipe connections to the same peer, holding each of them in a different peergroup context for security reasons. Messages can be sent to the peer with different security levels depending on the pipe used.
Pipes are used behind the scenes for many shell services. One such service is the talk service, which allows users in two different shells to send simple string messages to each other.
To use the talk service, you must register two users (either in the same or -- ideally -- different shells). One registration looks like this:
JXTA>talk -register sdo ...... User: sdo is now registered JXTA>talk -login sdo
Now repeat the process in a second shell with a different user:
JXTA>talk -register jra ...... User: jra is now registered JXTA>talk -login jra
In the first shell, you can then send a message from
JXTA>talk -u sdo jra found user's advertisement attempting to connect talk is connected to user jra Type your message. To exit, type "." at beginning of line Hello!
In the second shell, you'll see this message:
talk: from sdo to jra Message: Hello!
Behind the scenes, this example uses a number of JXTA services; the one that concerns us for now is the pipe service. The message from
jra is sent via a pipe. What happens is this:
A user is registered. This creates an advertisement that the peer will accept talk messages.
The user logs in. This creates the actual input pipe. A thread is set up to read continually from the input pipe, which is why the message from
jraappeared asynchronously in the second shell window. This is why logging in is a necessary step in this service; without it, there would be no input pipe to accept messages.
The user sends a message. This creates an output pipe; whatever data is written to this output pipe will be read on the input pipe of the user to whom the message is directed.
We can explore some other shell commands to understand a little more about how JXTA pipes work. To create a pipe, we must make a pipe advertisement and use it to create the pipes:
JXTA>pipeadv = mkadv -p JXTA>inpipe = mkpipe -i pipeadv JXTA>outpipe = mkpipe -o pipeadv
This creates both an input and output pipe. In order for the pipes to be connected to each other, they must be created with the same advertisement that we created here. In the talk service (and in other JXTA applications), you discover input pipe advertisements that are created by the shell in response to the
talk -register command. The shell uses this pipe advertisement to create the input pipe in response to the
talk -login command; it uses this pipe advertisement to create the output pipe in response to the
talk user command.
In this example, we've created the input and output pipes in the same peer. Later, we'll see how to create the pipes in different peers.
The information transmitted through pipes is messages. Messages define an envelope to transfer any kind of data: text, code, and so on. Furthermore, a message may contain an arbitrary number of uniquely named sections. Each section has an associated MIME type and can hold any form of data. Binary data may be encoded using the Base64 encoding scheme in the body of a section; a CDATA section may also be used. Some sections may contain XML-structured documents. Applications and services communicate by constructing messages and sending and/or receiving messages through the input and output pipe endpoints.
JXTA messages use a binary format to enable the efficient transfer of binary and XML data. A JXTA binary message format is composed of a sequence of elements. Each element has a name and a MIME type and can contain either binary or XML data. The form of a JXTA message is shown in Figure 2-6.Figure 2-6. A JXTA message
In order to send data over a pipe (or to any JXTA peer), the data must be encapsulated in a message. In the shell, this can be done by importing an existing file that contains the message body and using the
mkmsg command to convert the data into a message. When this is done, the imported data will be associated in the message with a tag of your choosing.
First, we need to create a file containing the message data. Such a file would contain a set of arbitrary XML tags; a simple example is the file containing the single line:
If this file is named data, then we can import it into the shell like this:
JXTA>importfile -f data mydata
mydata variable now contains the data read in from the file. In fact, it contains other information as well, since the shell has embedded some additional XML into it.
You can create the actual message like this:
JXTA>mymsg = mkmsg JXTA>put mymsg mytag mydata
The first command creates an empty message named
mymsg; the second populates this message with the data from the
mydata object, associating it with the
If you've made the pipes and message, you can then send and receive data like this:
JXTA>send outpipe mymsg JXTA>newmsg = recv inpipe recv has received a message
The message is sent on the output pipe. The input pipe then reads the message and stores it in a new message variable,
newmsg. The contents of
newmsg are the same as those of
mymsg; they have a tag of
mytag and an associated message body that contains the structured XML document created by reading the datafile. The message body can be extracted with the
JXTA>newdata = get newmsg mytag JXTA>cat newdata <?xml version="1.0"> <ShellDoc> <Item> <Data>Hello, world!</Data> </Item> </ShellDoc>
In the next and final installment, learn about advertisements.
Scott Oaks is a Java Technologist at Sun Microsystems, where he has worked since 1987. While at Sun, he has specialized in many disparate technologies, from the SunOS kernel to network programming and RPCs.
Bernard Traversat is a well-known developer in the Java Community and an active member of the Project JXTA. Bernard is the Engineering Manager for the JXTA CORE.
Li Gong is a well-known developer in the Java Community and an active member of the Project JXTA. Li is the JXTA Engineering Director for the JXTA CORE.
 In order to send a message to an output pipe endpoint, the corresponding input pipe endpoint must have been created first. This is a limitation of the current JXTA v1.0 implementation; it is expected to be removed in the future.
Return to ONJava.com.