- Protocol and API
- JXTA Goals
- JXTA Peer and Java
- Overview of the JXTA Protocols JAVA API
- Summary of Java API for JXTA Protocols
- Where JXTA Applications Begin
- The Peer
- Starting JXTA
- Peer Discovery Protocol API
- Peer Resolver Protocol API
- Peer Information Protocol
- Peer Membership Protocol
- Pipe Binding Protocol API
- Peer Endpoint Protocol
Peer Discovery Protocol API
Peer discovery is one of the more important tasks performed by a JXTA application. Most of the time, the peers, groups, and other information is not known until a peer uses the discovery service. An example of this is the shell application. The shell discovers all of its information and has no starting information about the network.
Applications may have some built-in knowledge, such as well-known peers, peer groups, and other information. However, most peers know very little about what is available. A peer may also not know the peers or the data they manage until they are discovered.
In the Java API, advertisements that are discovered are stored locally. Because of the local cache, there are two types of discoverylocal and remote. The remote discovery uses the resolver to find advertisements, while the local discovery uses the cache.
Classes in the Peer Discovery API
The discovery API is implemented as a service to peer groups. The API consists of the following key parts:
DiscoveryServiceThis is the base interface that is used to access core functionality of the peer discovery protocol.
DiscoveryListenerListener interface used to wait for remote discovery messages.
DiscoveryEventThe event passed to the listener that contains information about the discovered advertisements.
DiscoveryResponseMsgThe actual data payload that contains information about the discovered advertisements.
DiscoveryServiceImplImplementation of the DiscoveryService interface.
A high-level UML class diagram of these classes is shown in Figure 3.4.
The discovery API uses other classes like Cm, which is an implementation of a content manager. The Cm class is used to manage JXTA advertisements discovered. Cm persists messages to search and retrieve advertisements. In addition, the discovery API uses other APIs implementing other JXTA protocols, such as the peer resolver protocol used to send discovery queries to other peers in the JXTA network.
Figure 3.4 UML diagram of important classes in the Discovery Service API.
Accessing the Discovery Protocol
The discovery protocol takes place in the context of a peer group, so you need to have an instance of the PeerGroup interface. Peer groups are very important to discovery, because the group is a context that limits the scope of search. This means that if you are looking for peer advertisements, the only peers you should see are those that have joined your group. The same goes for subgroups, and all other advertisements created in the context of the group.
Now that you know that the group limits the discovery, you should understand that the World group is the first group you are given by the JXTA platform. The actual group you are in when you complete initialization of the platform it Net group, but World is the scope for discovery.
The Peer Group Interface
The peer group interface is a grab bag of methods that are associated with the information in the PeerGroupAdvertisement and other XML documents that describe features of the group. The interface is the primary access point to services. The services are either base services or services like the discovery service, which operate in the context of the group. In other words, if you access a service from a group, the service only works with peers in that group. This should be the preferred behavior of any service accessed from a specific group. The reason for isolation to the group is to limit the number of peers associated with actions enacted by peers that belong to the group.
The PeerGroup interface is a critical entry point to many of the services available in a peer group, so we will take a little more time to talk about the methods in the interface. Figure 3.5 shows the complete signature of the PeerGroup interface, and the following are summaries of the methods available in PeerGroup interface:
getLoaderReturns the JxtaLoader object from which this peer group was created and launched. JxtaLoader is an extension of Java's ClassLoader class.
isRendezvousReturns a Boolean true if this instance of the group is a rendezvous peer.
getPeerGroupAdvertisementReturns the group's PeerGroupAdvertisement.
getPeerAdvertisementReturns the PeerAdvertisement for this peer as a member of the group.
lookupServiceLook up a service by its name ID. Returns the service registered by the name specified.
compatibleReturns true if the given compatibility statement is loadable. This is required because a PeerGroupAdvertisement may be incompatible either by version or language that it supports.
loadModuleLoads a given module. If the module is compatible and loadable, the object of the Module is created, initialized, and returned.
publishGroupForce publication of this group. Only useful if the group is being created from scratch and the PeerGroup advertisement has not been created beforehand. In such a case, the group must have been named and its description set before making the call.
newGroupMethods to create instantiate and initialize new groups.
getRendezVousServiceReturns the RendezvousService for the group.
getEndpointServiceReturns the EndpointService for this group.
getResolverServiceReturns the ResolverService for this group.
getDiscoveryServiceReturns the DiscoveryService object for this group.
getPeerInfoServiceReturns the PeerInfoService for this group.
getMembershipServiceReturns the MembershipService service for the group. Note that this is an instance of NullMembershipService or a customized implementation by default.
getPipeServiceReturns the PipeService for this group.
getPeerGroupIDGets the ID of this group.
getPeerIDGets the PeerID of this peer in the context of this group.
getPeerGroupNameGets the Name of this group.
getPeerNameGets the Name of this peer in this group.
getConfigAdvertisementReturns the configuration advertisement if any exist.
getAllPurposePeerGroupImplAdvertisementGets an all-purpose peerGroup ModuleImplAdvertisement compatible with this group. The advertisement is initialized with all the data of a default peer group.
Figure 3.5 UML for PeerGroup interface.
Peer Group Inheritance
The classes and interfaces that make up the peer group API include several different layers used to isolate different aspects of the PeerGroup interface. Figure 3.6 shows the implementation and extensions of interface and classes to create the platform class.
Figure 3.6 UML for PeerGroup interface.
When you first look at this stack, the reasons for so much abstraction is not immediately obvious. First, you have the PeerGroup interface, which gives you the core functionality as defined by the JXTA specification. The RefPeerGroup interface is primarily used to ensure that this implementation is isolated from any other. The primary reason you need this interface is to associate the reference implementation from any others that may be created. In addition, the interface adds the ability to discover a parent peer group if one exists.
The GenericPeerGroup is an abstract class that is used to further add to the reference platform for peer groups. In this class are additional methods used to ensure compatibility and add the ability to create a specific advertisement that is compatible with the reference group. This class also defines most of the static final constants used to access services.
The StdPeerGroup class is an abstraction with which you will most likely interact. The class implements most of the functionality required by a peer group.
The Platform class is the final concrete implementation of the PeerGroup and represents the World peer group. This class is instantiated as JXTA is started. The platform class is then used to for discovery and communications with other peers in the network. The platform group is also used as a parent to other groups.
Local discovery is simply a method to search the locally cached advertisements in the cm directory. The following method of the DiscoveryService class does the work:
getLocalAdvertisements(int type, String attribute,String value)
The getLocalAdvertisements method will return an enumeration of base type Advertisement. The parameter type is an integer that corresponds to the constants in the DiscoveryService class shown in the following table.
Types of Document Returned
Peer group advertisements
All other advertisements including peer and group
The document type constants are used for all of the following methods discussed for the DiscoveryService class.
The attribute parameter in getLocalAdvertisement is a string that matches a tag in the XML representing the Advertisement. Of course, the value parameter is the value of the contents between the tag, excluding leading and trailing spaces.
When the values for the attribute and the values are null, all of the specific type of advertisement will be retrieved.
The following is an example that searches for a peer named "Mariann":
The next example returns all advertisements (excluding Peer and Peer Group):
The final example returns all the groups already known by the peer:
Note that if there are no matches in the cache, an empty enumerator is returned.
One of the most important things to understand about local discovery is that it is based on remote discovery locating the advertisement first. If you don't run a remote search, you won't find any advertisements unless they are hardcoded by your implementation.
There are various advertisements returned. Figure 3.7 shows the many different types of advertisements and the classes that implement them. To access specific elements of these advertisements, you will need to cast the advertisement to the appropriate type. Note that there are API and implementation versions.
Figure 3.7 UML of Advertisements classes used in JXTA Java API.
Local Cache Created versus Remote Discovery
You should always run local discovery first. The reason is that remote discovery can take seconds to several minutes to locate advertisements specified by your search. By using the local cache, you make your applications run faster by avoiding network searches each time you need an advertisement.
Remote discovery is a process where one or more peers are queried for advertisements. Remote discovery is initiated by the getRemoteAdvertisements method found in DiscoveryService. The following method sends queries to rendezvous to search their local database for the advertisements that match the pattern specified:
int getRemoteAdvertisements( String peerid, int type, String attribute, String value, int threshold );
The parameters of this method are used as follows:
peeridA specific peer ID that created the advertisement. This parameter may be null to obtain advertisements from any peer.
typeThe type of advertisement (PEER, GROUP, ADV).
attributeAn XML tag name that matches one in the advertisement. The attribute may be null, which selects all advertisements that match the type and the peerid parameters.
valueThe value of the contents of the tag enclosed by the tag specified by the attribute parameter. This value may be null if the attribute tag is null. If not null, the only advertisements returned are those that match the value.
thresholdThe upper limit of responses from one peer. This is an important variable, because the number of advertisements can be very large if the other parameters do not restrict the number of possible advertisements.
The threshold parameter, in addition to limiting responses, is also a form of politeness in the P2P network. If the threshold is very high, you are utilizing a lot of resources from the rendezvous peer that is probably busy with other peers and its own operations. Remember that other peers share the discovery service.
This method is non-blocking. The method returns after threads begin the process of sending out queries to rendezvous. As answers are returned, the advertisements are stored in the local cache for later retrieval by the getLocalAdvertisements method.
This method is really only useful for populating the local cache periodically. The problem is that the peer really does not know when the advertisement has been found. The better method is the one described next that uses a listener to know exactly when an advertisement is found.
Remote Discovery with Listener
The next method is similar to the last, except that there is an additional parameter that passes in a listener. The DiscoveryListener interface is called each time a query response is returned from a rendezvous with an advertisement matching the other parameters in the method:
void getRemoteAdvertisements( String peerid, int type, String attribute, String value, int threshold, DiscoveryListener listener );
The DiscoveryListener interface has only one method DiscoveryEvent. The method is used to pass an event parameter of type DiscoveryEvent. You can see the complete spec for these in the UML diagram in Figure 3.8.
Figure 3.8 UML of DiscoveryEvent, DiscoveryListener, and DiscoveryResponseMsg.
The DiscoveryEvent object contains DiscoveryResponseMsg, which contains all of the advertisements returned from a specific rendezvous. Be careful not to call any of the set methods, because the methods are used for processing messages, not for manipulation by you. The only methods you should use in this class are the "get" methods. The getResponses method is the most important because it returns an enumeration of the advertisements found. You can also get the original query information via getQueryAttr, getQueryValue, and getDiscoveryType. The advertisement returned by the getPeerAdv is for the rendezvous from which peer the advertisements originated. The getDocument and getAdvertisementType methods are of little value because they are just the XML the XML document type of the response message.
Do not use the setter methods of the DiscoveryResponseMsg. They are only to be used by the DiscoveryService when building the DiscoveryResponseMsg object. Using these methods does not guarantee changes to the XML representation of the advertisements returned by the getResponses method.
Advertisements have an expiration that is managed by JXTA. There are default expiration times, and you can override the lifetimes of advertisements when you publish with the remotePublish method and should not be set or overridden by other means.
Do not cache advertisements in application variables or use some other form of persistence, such as a database, to store advertisements. Only use the discovery mechanism and its cache. If you request an advertisement from the discovery cache when it has expired, you will not receive the advertisement in the results, and the current expired advertisement will be deleted. You should then rediscover the advertisement via the getRemoteAdvertisement method.
The publisher of an advertisement will automatically publish advertisements on a regular schedule as long as the peer is running. If the peer is shutdown, on restarting, it may publish the advertisements if the interval has expired. The only way to stop this is to flush the local version of the advertisement.
If a peer is not running, any advertisements that have propagated to other peers will eventually expire. If your peer is down longer than the expiration, the time to locate its published advertisements may be unnaturally long. If your peer is off for long periods, you should use a reasonable timeout when you first publish the advertisement.
The default expirations for locally created advertisements is one year in most cases, but some have longer defaults or none at all in the case of a peer advertisement. When advertisements are remotely published, the default is 2 hours, but this can vary depending on the value set during publishing. Note that you should not change the expiration value of the advertisement. You should set the expiration only via the remotePublish method.
One of the critical management problems is handling advertisements that may be invalid. Part of the problem is that we just don't know when an advertisement is truly invalid. Either the peer or the member of a group is not currently on the JXTA network, or the advertisement is truly transient and the need for it is gone. The other problem that can occur is that by the time an advertisement is propagated to another peer, the need for it has expired or the peer that published it is already disconnected from the network.
To alleviate problems with invalid advertisement, you will need to manage how you treat a certain types of advertisements. You should start by avoiding communication with specific peers. This simply means that you write your application to rely on random peers in a group rather than a specific peer.
If you do need to contact a specific peer, you should create a mechanism to retry if there is a failure. In the case of multiple peers that are transient, you may want to use a specific peer that is more persistent to exchange information. For example, a set of PDA peers would use a set of relay peers to hold information until the target PDA is connected to the network. There are other possibilities; you just need to use a little imagination and some experimentation.
A big problem is a peer that is not operating or is disconnected from the network. You should probably characterize specific peers as being transient to avoid deleting advertisements that are probably still good. For example, a peer advertisement from a laptop is probably still valid, even though it cannot be contacted. Instead, the laptop may simply be disconnected from the network while moving to a new location. Your application should attempt to contact the laptop later.
If you are developing, try to use a very short lifetime for advertisements. You should also make sure that old advertisements are purged to ensure that your test peers are not using old data.
Removing or Flushing Old Advertisements
One of the important things to do in a JXTA application is housekeeping. The number of advertisements can get quite large if the P2P network is large. There is also the problem of invalid advertisements caused by development and testing. To ensure that the system is working correctly, you will need to delete advertisements. The mechanism is the DiscoveryService class with the following signature:
void flushAdvertisements( String id, int type ) throws IOException;
To use flushAdvertisement, you need to have both the ID and the type of advertisement. It probably seems logical that all you would need is the ID, but there is a good reason this is not true. Please remember that the content management system, used to maintain advertisements, does so by the primary types of peer, peer group, and general advertisement. Consequently, you have to tell the system where to look for the advertisement by its type (PEER, GROUP, ADV), as well as the ID.
The best time to flush advertisements is when you have an advertisement that no longer works. You should be careful, because just because an advertisement fails does not mean that the advertisement is to blame. It is just as possible that the peers associated with the advertisement are not available. The good news, though, is that you can always search again for the advertisement. The bad news is that it may be the exact same one and the peer still not available. As a result, you may want to try an advertisement a few times over a set period before flushing the advertisement.
Remember also that advertisements have a built-in expiration. Expiration and the deletion is hidden, and there is no reason for you to flush advertisements if they are expired. Expiration and expected lifetime should still be considered when flushing advertisements. The reason is that those advertisements that are more volatile (shorter expirations) are more likely to be invalid. As a result, when dealing with pipes, you are more likely to get an invalid advertisement. Conversely, a peer group advertisement is less likely to be bad, because peer groups have a long lifetime.
Something else of note in this method is that the ID is a string and not a class. The reason for this is again related to the cache manager that stores advertisements by using the ID as a file name. In our example of this method, flushing the advertisement of a peer, we convert the peer ID to a string:
discovery.flushAdvertisements(padv.getPeerID().toString() , Discovery.PEER);
Dealing with the Advertisements Discovered
One aspect about the way that JXTA was developed is that advertisements are not directly useable. To take advantage of an advertisement, you need to build another object that uses the advertisement as its initialization.
For each advertisement, you should find a class that accepts the ad in a constructor, init, or factory method. You will see many of these throughout this chapter and the rest of the book.
Also, not all of an advertisement is exposed by a get method. This is especially true for customized sections. To use the data, you need to call the getDocument method and retrieve the values of the tags that interest you.
Another slightly different aspect of advertisements is that they are not necessarily two-way. The fact that an advertisement has set methods does not guarantee that the getDocument method will return an advertisement that is equivalent to the object. This may become true in the future, but for now, be sure to use the AdvertisementFactory class.
Group Services and Discovery
An important aspect of discovery is that because of its scooping of searches to its group, the items used by services in the group are also constrained by the group. What this means is that you should be able to search for any type of advertisement and expect that advertisement to be compatible to your group context and, thus, your services.
Finding Other Group Members
Another nice feature of group scoping is finding other users. When looking for advertisements of type PEER, you should only receive advertisements of peers that belong to the group from which you are searching. Be careful to remember that not all peers are visible at all times, and that sometimes peers change their name, ID, or both.