The Service Implementation

Here’s our first implementation of the RestoPeer class (the code that provides the restaurant service). A RestoPeer is responsible for discovering and joining the RestoNet peergroup; if the peer cannot find the RestoNet peergroup, the peer will create it.

The RestoPeer class has a simple structure:

  1. Discover the existing RestoNet peergroup advertisement.

    1. Look for any previously discovered advertisements in the local cache.

    2. Send the PDP request to discover the advertisement.

      This may be done using a broadcast-type mechanism or by contacting known rendezvous peers, either of which is a synchronous call to the developer. Behind the scenes, the API will create a new thread that will listen for responses. As these responses come back, the background thread will read them and insert the responses into the peer’s local cache of advertisements.

    3. Wait a while for any responses to come back.

    4. Look again for responses in the local cache.

  2. If the existing RestoNet is not found, create a peergroup advertisement for it. Then create the peergroup based on that advertisement.

  3. If the existing RestoNet is found, use the discovered advertisement to join the peergroup.

Here’s the code necessary to perform these steps:

import java.io.*;
import java.net.*;
import java.util.*;

import net.jxta.peergroup.PeerGroup;
import net.jxta.peergroup.PeerGroupFactory;
import net.jxta.peergroup.PeerGroupID;
import net.jxta.exception.PeerGroupException;
import net.jxta.discovery.DiscoveryService;
import net.jxta.id.IDFactory;
import net.jxta.pipe.PipeService;
import net.jxta.protocol.PeerGroupAdvertisement;
import net.jxta.protocol.ModuleImplAdvertisement;


// RestoPeer represents a restaurant that receives auction requests
// for food from HungryPeers. The RestoPeer will discover and join
// the RestoNet and publish itself as a provider for HungryPeers

public class RestoPeer {

    private PeerGroup netpg = null;     // The NetPeerGroup
    private PeerGroup restoNet = null;  // The RestoNet peergroup

    private String brand = "Chez JXTA"; // Brand of my restaurants
    private int timeout = 3000;         // Time-out; can be adjusted

    // Services within the RestoNet peergroup
    private DiscoveryService disco;     // Discovery service
    private PipeService pipes;          // Pipe service

    static String groupURL =
          "jxta:uuid-4d6172676572696e204272756e6f202002";

    public static void main(String args[]) {
        RestoPeer myapp = new RestoPeer(  );
        myapp.startJxta(  );
        System.exit(0);
    }

    // Start the JXTA application
    private void startJxta(  ) {
        try {
            // Discover and join (or start) the default peergroup
            netpg = PeerGroupFactory.newNetPeerGroup(  );
        } catch (PeerGroupException e) {
            // Couldn't initialize; can't continue
            System.out.println(
                "Fatal error : creating the net PeerGroup:);
            System.exit(1);
        }

        // Discover (or create) and join the RestoNet peergroup
        try {
            joinRestoNet(  );
        } catch (Exception e) {
            System.out.println("Can't join or create RestoNet");
            System.exit(1);
        }

        // Wait for HungryPeers
        System.out.println("Waiting for HungryPeers");
        while (true) {
            // In later examples, HungryPeer requests are processed here
            // For now, we'll just block indefinitely
            synchronized(this) {
                try { wait(  ); } catch (InterruptedException ie) {}
            }
        }
    }

    // Discover (or create) and join the RestoNet peergroup
    private void joinRestoNet(  ) throws Exception {

        int count = 3;   // Maximun number of attempts to discover
        System.out.println(
            "Attempting to Discover the RestoNet PeerGroup");

        // Get the discovery service from the NetPeergroup
        DiscoveryService hdisco = netpg.getDiscoveryService(  );

        Enumeration ae = null;  // Holds the discovered peers

        // Loop until we discover the RestoNet or
        // until we've exhausted the desired number of attempts
        while (count-- > 0) {
            try {
                // Search first in the peer's local cache to find
                // the RestoNet peergroup advertisement
                ae = hdisco.getLocalAdvertisements(
                    DiscoveryService.GROUP, "Name", "RestoNet");

                // If we found the RestoNet advertisement, we are done
                if ((ae != null) && ae.hasMoreElements(  ))
                    break;

                // If we did not find it, we send a discovery request
                hdisco.getRemoteAdvertisements(null,
                       DiscoveryService.GROUP, "Name",
                       "RestoNet", 1, null);

                // Sleep to allow time for peers to respond to the
                // discovery request
                try {
                    Thread.sleep(timeout);
                } catch (InterruptedException ie) {}
            } catch (IOException e) {
                // Found nothing! Move on.
            }
        }

        PeerGroupAdvertisement restoNetAdv = null;

        // Check if we found the RestoNet advertisement. If we didn't, then
        // either we are the first peer to join or no other RestoNet peers
        // are up. In either case, we must create the RestoNet peergroup.

        if (ae == null || !ae.hasMoreElements(  )) {
            System.out.println(
                 "Could not find the RestoNet peergroup; creating one");
            try {

                // Create a new, all-purpose peergroup
                ModuleImplAdvertisement implAdv =
                    netpg.getAllPurposePeerGroupImplAdvertisement(  );

                restoNet = netpg.newGroup(
                           mkGroupID(  ),       // Assign new group ID
                           implAdv,           // The implementation
                                              // advertisement
                           "RestoNet",        // Name of peergroup
                           "RestoNet, Inc."); // Description of peergroup

                // Get the peergroup advertisement
                restoNetAdv = netpg.getPeerGroupAdvertisement(  );

            } catch (Exception e) {
                System.out.println(
                    "Error in creating RestoNet Peergroup");
                throw e;
            }
        } else {
            // The RestoNet advertisement was found in the cache;
            // this means we can join the existing RestoNet peergroup

            try {
                restoNetAdv = (PeerGroupAdvertisement) ae.nextElement(  );
                restoNet = netpg.newGroup(restoNetAdv);
                System.out.println(
                     "Found the RestoNet Peergroup advertisement; " +
                     "joined existing group");
            } catch (Exception e) {
                System.out.println("Error in creating RestoNet " +
                           "PeerGroup from existing adv");
                throw e;
            }
        }

        try {
            // Get the discovery and pipe services for the RestoNet
            // peergroup (unused in this example)
            disco = restoNet.getDiscoveryService(  );
            pipes = restoNet.getPipeService(  );
        } catch (Exception e) {
            System.out.println("Error getting services from RestoNet");
            throw e;
        }

        System.out.println(
            "RestoNet Restaurant (" + brand + ") is on-line");
        return;
    }

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

As with all JXTA applications, the first thing the RestoPeer must do is discover and join the NetPeerGroup. Once it has joined this peergroup, it can use the services of this peergroup. In this case, it uses the discovery service (hdisco) of the NetPeerGroup object to look for the RestoNet peergroup advertisement. We follow the standard steps outlined above. Check the local cache. If nothing is found, use the PDP to find the advertisement and sleep for a while to give the advertisement a chance to be discovered.

We try to discover the advertisement three times. Due to the unpredictable nature of the network, there is no way to tell how long it will take to find an advertisement. The number of attempts depends on your network environment: you may have to adjust the number of tries if you are running through multiple hops, or you may need to sleep longer to allow the message to come back. In general, these decisions must be made on an application-specific basis.

Note that this example uses JXTA’s synchronous discovery API. The JXTA API also provides an asynchronous mechanism that an application can use to be notified when a resource is discovered (we show this API in Chapter 5). The synchronous discovery API provides a fine-grained control over the discovery process, while the asynchronous API is easier to manage.

Since we are looking for a peergroup advertisement, the Discovery.GROUP type is used in the getLocalAdvertisements( ) method. The Name tag field of the peergroup advertisement is used to search for the peergroup. We are looking for a peergroup advertisement in which the Name tag has a value equal to “RestoNet.” Any peergroup advertisements held in the local cache and with a matching type and attribute/value pair will be returned by this method. If such an advertisement is found, we are done, and we exit from the discovery loop.

If we did not find the advertisement in the local cache, a discovery request is sent within the NetPeerGroup via the getRemoteAdvertisements( ) method to find the RestoNet advertisement. Since the peer ID is null, the discovery request is propagated to all peers in the current subnet and to the known rendezvous peers for the NetPeerGroup. The threshold argument for the request is set to 1, since there is only one RestoNet peergroup advertisement.

After the discovery request is sent, we sleep for a while before checking the cache to see if some responses were received. When a response is received, the advertisement is automatically stored in the local cache, but we need to allow some time for that process to happen.

Note that a new remote discovery request is sent during each iteration. We must send multiple requests since the JXTA network does not guarantee that a discovery request will be processed by any peers. A rendezvous peer may be overloaded and discard the request. A new rendezvous may be available at the time of the request; as the JXTA network grows, more peers may be willing to respond.

If after three iterations the RestoNet advertisement was not found, then we continue on and create the RestoNet peergroup. We may be the first peer trying to join the RestoNet group, or all the other peers that know about the RestoNet group may be down or unreachable. We don’t know how many other peers are out there, or the state of any previous RestoNet peers; we simply know that, at this point in time, we cannot find the RestoNet peergroup.

Creating the Peergroup

If the RestoNet advertisement is not found, we must create a new RestoNet peergroup. We use the getAllPurposePeerGroupImplAdvertisement( ) method of the PeerGroup class to create a peergroup advertisement. This creates a generic advertisement; it automatically sets all of the attributes of the peergroup advertisement based on the NetPeerGroup (or, more generally, based on whichever peergroup is being used as the parent of the new peergroup).

The peergroup advertisement can then be passed to the newGroup( ) method of the NetPeerGroup. This method will create the new peergroup and send information about the new peergroup to the JXTA network (so that other peers can discover the newly created group). This is called publishing the advertisement; the newGroup( ) method publishes automatically, but you generally must explicitly publish other advertisements.

If we discovered an existing RestoNet advertisement, then the procedure is slightly altered: the RestoNet advertisement itself is used to instantiate the peergroup (though it still calls the newGroup( ) method of the NetPeerGroup). In this case, the newGroup( ) method will join the existing RestoNet peergroup; it will not need to publish the RestoNet advertisement as that advertisement has already been published.

The peergroup ID

It is important to point out that if peers cannot find the RestoNet peergroup, they need to create the same RestoNet peergroup; we don’t want the situtation in which different peers create different versions of the RestoNet peergroup. This is done by ensuring that the peergroup ID generated by each peer is the same.

In our examples, we do this by using the makeGroupID( ) method in conjunction with a predefined string from which the URL defining the peergroup ID is generated. The string can be obtained via the mkpgrp command of the JXTA Shell by following these commands:

JXTA>mkpgrp dummygroup
JXTA>groups
group0: name = dummygroup
JXTA>cat group0
<?xml version="1.0"?>

<!DOCTYPE jxta:PGA>

<jxta:PGA xmlns:jxta="http://jxta.org">
    <GID>
        urn:jxta:uuid-4d6172676572696e204272756e6f202002
    </GID>
    <MSID>
        urn:jxta:uuid-DEADBEEFDEAFBABAFEEDBABE000000010306
    </MSID>
    <Name>
        dummygroup
    </Name>
    <Desc>
       Created by mkpgrp
    </Desc>
</jxta:PGA>

Get JXTA in a Nutshell now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.