The JailGameClient class implements the Runnable Interface, and sets up the jail community and publishes its JXTA group advertisement remotely, as well. The JailGameClient class employs the following methods:
Listing 21.1 presents the code for JailGameClient.java. Listing 21.1 JailGameClient.java.import java.util.*; import java.io.*; import net.jxta.id.IDFactory; import net.jxta.pipe.InputPipe; import net.jxta.pipe.PipeMsgEvent; import net.jxta.pipe.PipeMsgListener; import net.jxta.credential.AuthenticationCredential; import net.jxta.document.StructuredDocument; import net.jxta.document.Document; import net.jxta.document.StructuredTextDocument; import net.jxta.document.MimeMediaType; import net.jxta.membership.Authenticator; import net.jxta.membership.MembershipService; import net.jxta.peergroup.PeerGroup; import net.jxta.peergroup.PeerGroupFactory; import net.jxta.exception.PeerGroupException; import net.jxta.exception.ProtocolNotSupportedException; import net.jxta.document.AdvertisementFactory; import net.jxta.document.MimeMediaType; import net.jxta.discovery.DiscoveryService; import net.jxta.discovery.DiscoveryListener; import net.jxta.discovery.DiscoveryEvent; import net.jxta.pipe.PipeService; import net.jxta.pipe.OutputPipe; import net.jxta.pipe.OutputPipeListener; import net.jxta.pipe.OutputPipeEvent; import net.jxta.protocol.DiscoveryResponseMsg; import net.jxta.protocol.PipeAdvertisement; import net.jxta.protocol.PeerGroupAdvertisement; import net.jxta.protocol.ModuleImplAdvertisement; import net.jxta.protocol.PeerAdvertisement; import net.jxta.protocol.ResolverQueryMsg; import net.jxta.protocol.ResolverResponseMsg; import net.jxta.resolver.ResolverService; import net.jxta.resolver.QueryHandler; import net.jxta.endpoint.Message; import net.jxta.endpoint.MessageElement; import net.jxta.document.Advertisement; import net.jxta.id.ID; import net.jxta.impl.protocol.ResolverResponse; import net.jxta.impl.protocol.ResolverQuery; /** * This game places players (peers) in a jail (group). In order to escape * jail, the player must request for the jail token. Once obtained, * the player may leave, but before leaving, the player must leave the * token behind with another player. * * After the player leaves, he can not return. It would be easy to * restructure this game to enable players to return to the jail, but once * you leave jail, who in their right mind would want to go back? * * Whoever creates the jail automatically creates the only key and places * himself in jail. All others may join the jail, but they do not have * the key. The key is not required to enter jail in this game. * * The game demonstrates how messages are passed from peer to peer using * direct connections as well as broadcast methods using the ResolverService. */ public class JailGameClient implements Runnable { public static final String JAIL_GROUP = "JAIL"; public static final String JAIL_GROUP_DESCRIPTION = "Jail description"; public static final String JAIL_KEY_QUERY_HANDLER = "jail_key_query_handler"; public static final String JAIL_KEY_LOCATOR_HANDLER = "jail_key_locator_handler"; public static final String JAIL_TRANSFER_KEY = "jail_transfer_key"; public static final int JOIN_GAME = 0; public static final int CREATE_GAME = 1; private PeerGroup netPeerGroup; private PeerGroup jailPeerGroup; private DiscoveryService discoverySvc; private boolean pipeAdvFound = false; private int createOrJoin = -1; private boolean groupFound = false; private PipeAdvertisement jailUnicastPipeAdv; private PipeMsgListener unicastListener; private InputPipe inputUnicastPipe; private boolean inJail = false; private boolean hasJailToken = false; private String jailTokenString; private Set jailPeerSet = new HashSet(); public JailGameClient(int createOrJoin) { this.createOrJoin = createOrJoin; } /** * Let a peer join a group. */ private boolean joinPeerGroup(PeerGroup grp) { StructuredDocument creds = null; try { AuthenticationCredential authCred = new AuthenticationCredential(grp, null, creds); MembershipService membershipSvc = grp.getMembershipService(); Authenticator auth = membershipSvc.apply(authCred); if (auth.isReadyForJoin()) { membershipSvc.join(auth); this.inJail = true; return true; } else { String errMsg = "Unable to join group:" + grp.getPeerGroupName(); System.out.println(errMsg); return false; } } catch (Exception e) { String errMsg = "Unable to join group: " + grp.getPeerGroupName(); System.out.println(errMsg); e.printStackTrace(); return false; } } /** * Advertise peer groups. */ private boolean joinPeerGroup(PeerGroupAdvertisement adv) { try { return joinPeerGroup(netPeerGroup.newGroup(adv)); } catch (Exception e) { String errMsg = "Unable to create group from advertisement."; System.out.println(errMsg); e.printStackTrace(); return false; } } /** * Create peer groups based on advertisements. */ private PeerGroup createGroup(String name) throws PeerGroupException, Exception { ModuleImplAdvertisement implAdv = netPeerGroup.getAllPurposePeerGroupImplAdvertisement(); PeerGroup jailGroup = netPeerGroup.newGroup(null, implAdv, JAIL_GROUP, JAIL_GROUP_DESCRIPTION); return jailGroup; } /** * Publishes a pipe advertisement for the transfer of the jail token. */ private void createJailUnicastPipeAdv() throws IOException { PipeAdvertisement pipeAdv = null; pipeAdv = (PipeAdvertisement) AdvertisementFactory. newAdvertisement( PipeAdvertisement.getAdvertisementType()); pipeAdv.setPipeID(IDFactory.newPipeID (jailPeerGroup.getPeerGroupID())); pipeAdv.setName(JailGameClient.JAIL_TRANSFER_KEY + ":" + jailPeerGroup. getPeerName()); pipeAdv.setType(PipeService.UnicastType); this.jailUnicastPipeAdv = pipeAdv; } /** * Publish advertisements locally or remotely. */ private void publishJailAdv(Advertisement adv, int type) throws IOException { DiscoveryService jailDiscoverySvc = jailPeerGroup.getDiscoveryService(); jailDiscoverySvc.publish(adv, type); jailDiscoverySvc.remotePublish(adv, type); } /** * Publishes an advertisement locally and remotely with no lifetime. */ private void publish(Advertisement adv, int type) throws IOException { discoverySvc.publish(adv, type); discoverySvc.remotePublish(adv, type); } /** * Prints the members of the jail group. */ private void printPeers() { refreshPeerSet(); try { Thread.sleep(10 * 1000); // may want to sleep longer. } catch (Exception ignore) { } Object [] peers = jailPeerSet.toArray(); for (int x = 0 ; x < peers.length ; x++) { System.out.println("Peer: " + ((PeerAdvertisement)peers[x]).getName()); } } /** * This call is asynchronous and uses a DiscoveryListener to process * the peer advertisements. */ private void refreshPeerSet() { jailPeerSet.clear(); System.out.println("Searching for peers"); DiscoveryService jailDiscoverySvc = jailPeerGroup.getDiscoveryService(); try { jailDiscoverySvc.flushAdvertisements(null, DiscoveryService.PEER); discoverySvc.flushAdvertisements(null, DiscoveryService.PEER); } catch (IOException io) { System.out.println("Trouble flushing PEER advertisements"); io.printStackTrace(); } DiscoveryListener listener = new DiscoveryListener() { public void discoveryEvent(DiscoveryEvent ev) { DiscoveryResponseMsg res = ev.getResponse(); String aRes = res.getPeerAdv(); PeerAdvertisement peerAdv = null; try { // create a peer advertisement InputStream is = new ByteArrayInputStream ( (aRes).getBytes() ); peerAdv = (PeerAdvertisement) AdvertisementFactory. newAdvertisement(new MimeMediaType ( "text/xml" ), is); jailPeerSet.add(peerAdv); } catch (Exception e) { e.printStackTrace(); } } }; jailDiscoverySvc.getRemoteAdvertisements(null, DiscoveryService.PEER, null, null, 10, listener); } /** * The discovery listener used needs to be synchronized. It takes * a long time to run sometimes. Furthermore, more than one peer * can return the same jail group advertisement. */ private void discoverJailGroup() throws IOException { if (groupFound) { String msg = "Already found the jail group"; System.out.println(msg); return; } while (!groupFound) { discoverySvc.getRemoteAdvertisements(null, DiscoveryService.GROUP, "Name", JailGameClient.JAIL_GROUP, 1, null); try { //Wait ten seconds for advertisements to come in. Thread.sleep (10 * 1000); } catch (Exception ignore) { } Enumeration enum = discoverySvc. getLocalAdvertisements(DiscoveryService.GROUP, "Name", JailGameClient. JAIL_GROUP); //Cycle through all the advertisements cached locally. while (enum.hasMoreElements()) { //We want to discover the jail group. Advertisement ad = (Advertisement) enum.nextElement(); if (ad instanceof PeerGroupAdvertisement) { try { System.out.println("Creating the group."); jailPeerGroup = netPeerGroup.newGroup(ad); System.out.println("Done with group creation."); groupFound = true; return; } catch (PeerGroupException e) { e.printStackTrace(); } } } System.out.print("."); } } /** * Sets up the broadcast and unicast pipe and its listeners. */ private void createPipes() throws IOException { PipeService pipeSvc = jailPeerGroup.getPipeService(); if (unicastListener == null) { //Create a message listener to accept the jail token. unicastListener = new PipeMsgListener() { public void pipeMsgEvent(PipeMsgEvent pipeMsgEvent) { //We received a message. Message message = pipeMsgEvent.getMessage(); if (message.hasElement("key") && message.hasElement("sender")) { //Determine the sender and the key. MessageElement element = message.getElement("key"); InputStream stream = element.getStream(); MessageElement senderElement = message.getElement("sender"); InputStream senderStream = senderElement.getStream(); try { byte [] bytes = new byte[stream.available()]; stream.read(bytes); jailTokenString = new String(bytes); hasJailToken = true; bytes = new byte[senderStream.available()]; senderStream.read(bytes); String sender = new String(bytes); String msg = "Received key " + jailTokenString + " from peer " + sender; System.out.println(msg); } catch (IOException io) { io.printStackTrace(); } finally { try { stream.close(); senderStream.close(); } catch (Exception ignore) { } } } else { //Ignore this message. } } }; } //Create the actual pipe based on the advertisement //and the listener. this.inputUnicastPipe = pipeSvc.createInputPipe(jailUnicastPipeAdv, unicastListener); } /** * Sets up two QueryHandler listeners. * One for the jail token requestor. * Another for the jail token locator. */ private void setResolverHandler() { System.out.println("Setting up the resolver handlers"); ResolverService jailResolverService = jailPeerGroup.getResolverService(); //Create a handler to process all incoming queries, //and process reponses to any queries we send. QueryHandler handler = new QueryHandler() { /** * Processes any incoming queries. If this client has * the key, we will send it to the requestor. * Otherwise, we will send a "Sorry" response. */ public ResolverResponseMsg processQuery (ResolverQueryMsg query) { //System.out.println("Key Requestor Query Handler: " + //query.getQuery()); if (JailGameClient.this.hasJailToken) { String msg = "Sending jail token to another peer"; System.out.println(msg); } String response = null; //If we have the key, we'll send it. //Otherwise, we'll send a "Sorry" response. response = (JailGameClient.this.hasJailToken) ? JailGameClient.this. jailTokenString : "Sorry"; JailGameClient.this.hasJailToken = false; JailGameClient.this.jailTokenString = null; //Sending our response. return new ResolverResponse (JailGameClient.JAIL_KEY_QUERY_HANDLER, "JXTACRED", query.getQueryId(), response); } /** /* Process responses to our search queries for the * jail token. If we receive any non-"Sorry" responses, * we receive the token. */ public void processResponse(ResolverResponseMsg responseMsg) { String response = responseMsg.getResponse(); //System.out.println ("Key Requestor Query Handler: " + response); if (!response.equals("Sorry")) { System.out.println("Received jail token."); hasJailToken = true; jailTokenString = response; } } }; //Register the handler. jailResolverService.registerHandler (JailGameClient.JAIL_KEY_QUERY_HANDLER, handler); //Create a handler to locate the key. QueryHandler keyLocatorHandler = new QueryHandler() { /** * Process queries requesting to see if we have the token. * If this peer has the token, respond with the * name of this peer. */ public ResolverResponseMsg processQuery (ResolverQueryMsg query) { //System.out.println("Key Locator Query Handler: " + query.getQuery( )); String response = null; response = (JailGameClient.this.hasJailToken) ? JailGameClient.this. jailPeerGroup. getPeerName() : ""; return new ResolverResponse(JailGameClient. JAIL_KEY_LOCATOR_HANDLER, "JXTACRED", query.getQueryId(), response); } /** * Process responses to our search queries for the * token. If the response is not a blank string, the * response is the name of the peer that has the token. */ public void processResponse(ResolverResponseMsg responseMsg) { String response = responseMsg.getResponse(); //System.out.println ("Key Locator Query Handler: " + response); if (!response.equals("")) { System.out.println("Peer: " + response + " has the token"); } } }; //Register the handler. jailResolverService.registerHandler (JailGameClient.JAIL_KEY_LOCATOR_HANDLER, keyLocatorHandler); } /** * This method uses the resolver service to broadcast a request for * the jail token. It does not do the job of retrieving the token. */ private void requestJailToken() { if (this.hasJailToken) { System.out.println("I already have the token."); return; } System.out.println("Requesting the jail token"); ResolverService jailResolverService = jailPeerGroup.getResolverService(); ResolverQueryMsg query = null; query = new ResolverQuery(JailGameClient.JAIL_KEY_QUERY_HANDLER, "cred", jailPeerGroup.getPeerID(). toString(), "Key request", 1); jailResolverService.sendQuery(null, query); } /** * Does a broadcast using the resolver service to determine * the location of the jail token. */ private void findJailToken() { if (this.hasJailToken) { System.out.println("I have the token."); return; } ResolverService jailResolverService = jailPeerGroup.getResolverService(); ResolverQueryMsg query = null; query = new ResolverQuery(JailGameClient.JAIL_KEY_LOCATOR_HANDLER, "cred", jailPeerGroup.getPeerID(). toString(), "Key location request", 1); jailResolverService.sendQuery(null, query); } /** * Leaves jail if the peer has the key. Passes the jail token * to peerName. * @param peerName The peer name to pass the jail token to. */ private void leaveJail(String peerName) { if (!this.inJail) { System.out.println("You are not in jail"); return; } if (!hasJailToken) { System.out.println("You do not have the token"); return; } if (!transferJailKey(peerName)) { return; } System.out.println("Leaving Jail"); MembershipService membershipSvc = this.jailPeerGroup. getMembershipService(); //Leave the group and flush all local advertisements. try { membershipSvc.resign(); this.jailPeerGroup.stopApp(); try { this.discoverySvc.flushAdvertisements(null, DiscoveryService.GROUP); this.discoverySvc.flushAdvertisements(null, DiscoveryService.PEER); this.discoverySvc.flushAdvertisements(null, DiscoveryService.ADV); } catch (IOException ignore) { ignore.printStackTrace();} this.inJail = false; } catch (PeerGroupException pge) { pge.printStackTrace(); } this.cleanResources(); } /** * Quits the application only if the user has left jail. */ private void quit() { if (this.hasJailToken) { System.out.println("You have the jail token. " + "Existing players cannot leave the jail without first having the token and then transferring it."); } System.out.println("Quitting."); System.exit(0); } /** * Creates an output pipe to the peer indicated by peerID and sends * the jail token * * @return true If the transfer is successful, false otherwise. */ private boolean transferJailKey(String peerName) { if (!this.hasJailToken) { System.out.println("You do not have the jail token."); return false; } refreshPeerSet(); try { Thread.sleep(5 * 1000); } catch (Exception ignore) {} // Check to make sure the peer exists. Iterator iterator = jailPeerSet.iterator(); boolean peerExists = false; //Find the peer's PeerAdvertisement. while (iterator.hasNext()) { PeerAdvertisement adv = (PeerAdvertisement) iterator.next(); if (adv.getName().equals(peerName)) { peerExists = true; break; } } String msg = (!peerExists) ? "Warning: Peer " + peerName + " may not exist.\n" + "Attempting to transfer anyways." : "Transfering key to peer: " + peerName; System.out.println(msg); DiscoveryService jailDiscoverySvc = jailPeerGroup.getDiscoveryService(); DiscoveryListener listener = new DiscoveryListener() { /** * Creates the output pipe that connects to the peer we * wish to send the jail token to. Then sends the jail * token via the "key" element. */ public void discoveryEvent(DiscoveryEvent ev) { DiscoveryResponseMsg res = ev.getResponse(); Enumeration enum = res.getResponses(); while (enum.hasMoreElements()) { String advString = (String) enum.nextElement(); OutputPipe outputPipe = null; try { PipeAdvertisement pipeAdv = (PipeAdvertisement) AdvertisementFactory.newAdvertisement (new MimeMediaType("text/xml"), new ByteArrayInputStream (advString.getBytes())); PipeService pipeSvc = jailPeerGroup.getPipeService(); outputPipe = pipeSvc.createOutputPipe (pipeAdv, 1000); //Create the actual message with information //about the token and sender. Message message = pipeSvc.createMessage(); MessageElement messageElement = message.newMessageElement( "key", new MimeMediaType ("text/plain"), new ByteArrayInputStream (jailTokenString. getBytes())); message.addElement(messageElement); MessageElement senderElement = message.newMessageElement( "sender", new MimeMediaType("text/plain"), new ByteArrayInputStream (jailPeerGroup. getPeerName().getBytes())); message.addElement(messageElement); MessageElement senderElement = message.newMessageElement( "sender ",new MimeMediaType ("text/plain "),new ByteArrayInputStream (jailPeerGroup. getPeerName().getBytes())); message.addElement(senderElement); outputPipe.send(message); JailGameClient.this.hasJailToken = false; outputPipe.close(); return; } catch (Exception e) { e.printStackTrace(); } finally { if (outputPipe != null) { outputPipe.close(); } } } } }; String name = JailGameClient.JAIL_TRANSFER_KEY + ":" + peerName; jailDiscoverySvc.getRemoteAdvertisements(null, DiscoveryService.ADV,"name", name, 10, listener); // Now sleep for the transfer to finish try { Thread.sleep(10 * 1000); // we may want to sleep longer } catch (Exception ignore) { } return !this.hasJailToken; } /** * This method is used to clean up all the resources so we can start from scratch. */ private void cleanResources() { this.inputUnicastPipe.close(); this.unicastListener = null; this.jailPeerSet.clear(); this.groupFound = false; this.inJail = false; this.pipeAdvFound = false; this.hasJailToken = false; this.jailTokenString = null; ResolverService resolverSvc = this.jailPeerGroup. getResolverService(); resolverSvc.unregisterHandler (JailGameClient.JAIL_KEY_QUERY_HANDLER); resolverSvc.unregisterHandler (JailGameClient.JAIL_KEY_LOCATOR_HANDLER); this.jailPeerGroup = null; } /** * A utility method to print out the contents of an advertisement. */ public static void printAdvertisement(Advertisement adv) { try { Document doc = adv.getDocument (new MimeMediaType("text/plain")); ByteArrayOutputStream output = new ByteArrayOutputStream(); doc.sendToStream(output); System.out.println(output.toString()); // BufferedInputStream stream = new BufferedInputStream(doc.getStream()); // StringBuffer buffer = new StringBuffer(); // byte [] bytes = new byte[stream.available()]; // while ((c = stream.readChar())!= null) { // buffer.append(c); // } // System.out.println(buffer.toString()); } catch (Exception ignore) { ignore.printStackTrace(); } } /** * Start up the Net Peer group & get the Discovery Service for that group. */ public void startJxta() throws PeerGroupException, IOException { this.netPeerGroup = PeerGroupFactory.newNetPeerGroup(); this.discoverySvc = netPeerGroup.getDiscoveryService(); // Flush all local cache information. We want to start from scratch. discoverySvc.flushAdvertisements(null, DiscoveryService.ADV); discoverySvc.flushAdvertisements(null, DiscoveryService.PEER); discoverySvc.flushAdvertisements(null, DiscoveryService.GROUP); } public void run() { try { if (createOrJoin == JailGameClient.JOIN_GAME) { discoverJailGroup(); } else { // Create the group. // Add a propagate PIPE Advertisement to it. jailPeerGroup = createGroup(JailGameClient.JAIL_GROUP); this.hasJailToken = true; // The token is just the current time :) this.jailTokenString = "" + System.currentTimeMillis(); publish(jailPeerGroup.getPeerGroupAdvertisement(), DiscoveryService.GROUP); } // Publish the unicast pipe used to send the jail token // to others. createJailUnicastPipeAdv(); publishJailAdv(jailUnicastPipeAdv, DiscoveryService.ADV); } catch (Exception e) { String errMsg = "Unable to start create or join jail group."; System.out.println(errMsg); e.printStackTrace(); return; } if (!joinPeerGroup(jailPeerGroup)) { return; } try { this.createPipes(); } catch (Exception ex) { String errMsg = "Unable to create broadcast pipes"; System.out.println(errMsg); ex.printStackTrace(); return; } setResolverHandler(); //The user interface menu. for (;;) { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); String lineToSend = null; System.out.print("Type H for help\n:>"); try { while ((lineToSend = reader.readLine()) != null) { lineToSend = lineToSend.trim(); if (lineToSend.equals("")) { System.out.print(":>"); continue; } //If the peer is not in the group anymore, //say that to the peer. if (!inJail && !lineToSend.equalsIgnoreCase("Q")) { System.out.println("You are not in jail. You can only quit"); System.out.print(":>"); continue; } StringTokenizer tokenizer = new StringTokenizer(lineToSend); String command = tokenizer.nextToken(); if (command.equalsIgnoreCase("L")) { if (!tokenizer.hasMoreTokens()) { System.out.println("Peer name missing"); } else { String peerName = tokenizer.nextToken(); leaveJail(peerName); } } else if (command.equalsIgnoreCase("R")) { requestJailToken(); } else if (command.equalsIgnoreCase("P")) { printPeers(); } else if (command.equalsIgnoreCase("F")) { findJailToken(); } else if (command.equalsIgnoreCase("T")) { if (!tokenizer.hasMoreTokens()) { System.out.println("Client name missing"); } else { String peerName = tokenizer.nextToken(); transferJailKey(peerName); } } else if (command.equalsIgnoreCase("H")) { printHelp(); } else if (command.equalsIgnoreCase("Q")) { quit(); } System.out.print(":>"); } } catch (Exception io) { io.printStackTrace(); } } } public void printHelp() { String help = "H - print this help\nF - find who has the token\nR - request jail token\nP - print peers\nT <client> - transfer jail token to client\nL <client> - leave jail and send jail token to client\nQ - quit application\n"; System.out.println(help); } public static void main (String argc[]) throws Exception { if (argc.length < 1) { String msg = "Usage: " + JailGameClient.class.getName() + " [join/create]"; System.out.println(msg); return; } int createOrJoin = (argc[0].equals("join")) ? JailGameClient.JOIN_GAME : JailGameClient.CREATE_GAME ; JailGameClient client = new JailGameClient(createOrJoin); client.startJxta(); Thread thread = new Thread(client); thread.start(); } } |