/* * Copyright (c) 2004-2007 Sun Microsystems, Inc. All rights reserved. * * The Sun Project JXTA(TM) Software License * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. The end-user documentation included with the redistribution, if any, must * include the following acknowledgment: "This product includes software * developed by Sun Microsystems, Inc. for JXTA(TM) technology." * Alternately, this acknowledgment may appear in the software itself, if * and wherever such third-party acknowledgments normally appear. * * 4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA" must * not be used to endorse or promote products derived from this software * without prior written permission. For written permission, please contact * Project JXTA at http://www.jxta.org. * * 5. Products derived from this software may not be called "JXTA", nor may * "JXTA" appear in their name, without prior written permission of Sun. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SUN * MICROSYSTEMS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * JXTA is a registered trademark of Sun Microsystems, Inc. in the United * States and other countries. * * Please see the license information page at : * <http://www.jxta.org/project/www/license.html> for instructions on use of * the license in source files. * * ==================================================================== * * This software consists of voluntary contributions made by many individuals * on behalf of Project JXTA. For more information on Project JXTA, please see * http://www.jxta.org. * * This license is based on the BSD license adopted by the Apache Foundation. */ package tutorial.customgroupservice; import java.io.IOException; import java.net.URI; import java.util.NoSuchElementException; import java.util.Timer; import java.util.TimerTask; import java.util.logging.Level; import java.util.logging.Logger; import net.jxta.document.Advertisement; import net.jxta.document.AdvertisementFactory; import net.jxta.document.XMLDocument; import net.jxta.endpoint.EndpointAddress; import net.jxta.endpoint.EndpointService; import net.jxta.endpoint.Message; import net.jxta.endpoint.MessageElement; import net.jxta.endpoint.StringMessageElement; import net.jxta.exception.PeerGroupException; import net.jxta.id.ID; import net.jxta.logging.Logging; import net.jxta.platform.ModuleSpecID; import net.jxta.peergroup.PeerGroup; import net.jxta.platform.Module; import net.jxta.platform.ModuleClassID; import net.jxta.protocol.ConfigParams; import net.jxta.protocol.ModuleImplAdvertisement; import net.jxta.service.Service; /** * A very simple Peer Group Service. * <p/> * This service sends JXTA an annoucement message every few seconds via * JXTA endpoint service <tt>propagate()</tt>. It also listens for announcement * messages from other peers and prints a message on the console whenever it * receives one. * <p/> * The protocol for this service consists of JXTA messages sent via Endpoint * propagation. In order for this example to work you <i>must</i> enable at * least one message transport which supports broadcast/multicast. * <p/> * This gossip service implementation uses the <tt>assignedID</tt> * which is initialized in <tt>init()</tt> as the endpoint address for the * messages it sends and receives. Use of the <tt>assignedID</tt> as the * <tt>serviceParam</tt> is a common choice because it is gauranteed to be * unique within the PeerGroup and the <tt>assignedID</tt> * <tt>serviceParam</tt> is informally reserved for the service with that * <tt>assignedID</tt>. * <p/> * The messages exchanged by the gossip service contain two message * elements in the "<tt>gossip</tt>" namespace. "<tt>sender</tt>" contains a * <tt>String</tt> of the peer id of the message sender. "<tt>gossip</tt>" * contains a <tt>String</tt> of the gossip text which is being shared by the * sender. */ public class GossipService implements net.jxta.service.Service, net.jxta.endpoint.EndpointListener { /** * Logger */ private static final transient Logger LOG = Logger.getLogger(GossipService.class.getName()); /** * The module class ID for Gossip services. All Gossip services regardless * of the protocol used share this same module class id. */ public static final ModuleClassID GOSSIP_SERVICE_MCID = ModuleClassID.create(URI.create("urn:jxta:uuid-4CD1574ABA614A5FA242B613D8BAA30F05")); /** * The module spec ID for our Gossip service. The module spec id contains * the {@code GOSSIP_SERVICE_MCID}. All implementations which use the * same messaging protocol as this implementation will share this same * module spec id. */ public static final ModuleSpecID GOSSIP_SERVICE_MSID = ModuleSpecID.create(URI.create("urn:jxta:uuid-4CD1574ABA614A5FA242B613D8BAA30FD0A45F5F0E1A450DA912BB01585AB0FC06")); /** * The default gossip text we will send to other peers. */ public static final String DEFAULT_GOSSIP_TEXT = "JXTA is cool. Pass it on!"; /** * Whether we should show our own gossip text default. */ public static final boolean DEFAULT_SHOW_OWN = false; /** * The default interval in milliseconds at which we will send our gossip * text. */ public static final long GOSSIP_INTERVAL_DEFAULT = 10 * 1000L; /** * The name of the message namespace for all gossip service messages. */ public static final String GOSSIP_NAMESPACE = "gossip"; /** * The name of the message element identifying the gossip sender. */ public static final String GOSSIP_SENDER_ELEMENT_NAME = "sender"; /** * The name of the message element containing the gossip text. */ public static final String GOSSIP_GOSSIP_ELEMENT_NAME = "gossip"; /** * A Timer shared between all Gossip service instances that we use for * sending our gossip messages. */ public static final Timer SHARED_TIMER = new Timer("Gossip Services Timer", true); /** * The peer group in which this instance is running. */ private PeerGroup group; /** * Our assigned service ID. Usually this is our MCID but may also be our * MCID with a role id if there are multiple gossip services within the * peer group. */ private ID assignedID; /** * The module implementation advertisement for our instance. */ private ModuleImplAdvertisement implAdv; /** * The "gossip" message we read from our configuration. */ private String gossip = DEFAULT_GOSSIP_TEXT; /** * If {@code true} then we show our own gossip messages; */ private boolean showOwn = DEFAULT_SHOW_OWN; /** * The interval in milliseconds at which we will send our gossip message. */ private long gossip_interval = GOSSIP_INTERVAL_DEFAULT; /** * The endpoint service with which we send our gossips and register our * listener. */ private EndpointService endpoint = null; /** * The timer task we use to send our gossip messages. */ private TimerTask sendTask = null; /** * {@inheritDoc} * <p/> * This implementation doesn't currently use interface objects so it just * returns itself. We would use an interface object if we needed to maintain * state for each caller of the Gossip Service or wished to attach a * security context to the callers of this service. ie. different callers * might have different sercurity privleges. */ public Service getInterface() { return this; } /** * Return our assigned ID. * * @return Our assigned ID. */ public ID getAssignedID() { return assignedID; } /** * {@inheritDoc} */ public Advertisement getImplAdvertisement() { return implAdv; } /** * {@inheritDoc} */ public void init(PeerGroup group, ID assignedID, Advertisement implAdvertisement) throws PeerGroupException { this.group = group; this.assignedID = assignedID; this.implAdv = (ModuleImplAdvertisement) implAdvertisement; // Get our configuration parameters. GossipServiceConfigAdv gossipConfig = null; ConfigParams confAdv = group.getConfigAdvertisement(); if (confAdv != null) { Advertisement adv = null; try { XMLDocument configDoc = (XMLDocument) confAdv.getServiceParam(getAssignedID()); if (null != configDoc) { adv = AdvertisementFactory.newAdvertisement(configDoc); } } catch (NoSuchElementException failed) { //ignored } if (adv instanceof GossipServiceConfigAdv) { gossipConfig = (GossipServiceConfigAdv) adv; } } if (null == gossipConfig) { // Make a new advertisement for defaults. gossipConfig = (GossipServiceConfigAdv) AdvertisementFactory.newAdvertisement(GossipServiceConfigAdv.getAdvertisementType()); } // If the config has a non-null gossip then use that. if (null != gossipConfig.getGossip()) { gossip = gossipConfig.getGossip(); } // If the config has a non-null showOwn then use that. if (null != gossipConfig.getShowOwn()) { showOwn = gossipConfig.getShowOwn(); } if (Logging.SHOW_CONFIG && LOG.isLoggable(Level.CONFIG)) { StringBuilder configInfo = new StringBuilder("Configuring Gossip Service : " + assignedID); configInfo.append("\n\tImplementation :"); configInfo.append("\n\t\tModule Spec ID: ").append(implAdv.getModuleSpecID()); configInfo.append("\n\t\tImpl Description : ").append(implAdv.getDescription()); configInfo.append("\n\t\tImpl URI : ").append(implAdv.getUri()); configInfo.append("\n\t\tImpl Code : ").append(implAdv.getCode()); configInfo.append("\n\tGroup Params :"); configInfo.append("\n\t\tGroup : ").append(group); configInfo.append("\n\t\tPeer ID : ").append(group.getPeerID()); configInfo.append("\n\tConfiguration :"); configInfo.append("\n\t\tShow own gossips : ").append(showOwn); configInfo.append("\n\t\tGossip : ").append(gossip); LOG.config(configInfo.toString()); } } /** * {@inheritDoc} */ public synchronized int startApp(String[] args) { /* We require the peer group Endpoint service. Since the order in which * services are initialized is random the Endpoint might not yet be * initialized when we are first called. If the Endpoint service is not * available then we tell our caller that we can not yet start. The * peer group implementation will continue to start other services and * call our <tt>startApp()</tt> method again. */ endpoint = group.getEndpointService(); if (null == endpoint) { if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.warning("Stalled until there is an endpoint service"); } return Module.START_AGAIN_STALLED; } /* Register our listener for gossip messages. The registered address is * our assigned ID as a String as the <tt>serviceName</tt> and nothing * as the <tt>serviceParam</tt>. */ boolean registered = endpoint.addIncomingMessageListener(this, getAssignedID().toString(), null); if (!registered) { if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) { LOG.severe("Failed to regiser endpoint listener."); } return -1; } // Create our timer task which will send our gossip messages. sendTask = new TimerTask() { /** * {@inheritDoc} */ public void run() { sendGossip(); } }; // Register the timer task. SHARED_TIMER.schedule(sendTask, gossip_interval, gossip_interval); if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) { LOG.info("[" + group + "] Gossip Serivce (" + getAssignedID() + ") started"); } return Module.START_OK; } /** * {@inheritDoc} */ public synchronized void stopApp() { /* We have to assume that <tt>stopApp()</tt> might be called before * <tt>startApp()</tt> successfully completes. This means that fields * initialized in the <tt>startApp()</tt> method might not be * initialized. */ if (null != endpoint) { endpoint.removeIncomingMessageListener(getAssignedID().toString(), null); } endpoint = null; // Cancel our sending timer task. TimerTask currentTask = sendTask; if (null != currentTask) { currentTask.cancel(); } sendTask = null; if (Logging.SHOW_INFO && LOG.isLoggable(Level.INFO)) { LOG.info("[" + group + "] Gossip Serivce (" + getAssignedID() + ") stopped."); } } /** * {@inheritDoc} */ public void processIncomingMessage(Message message, EndpointAddress srcAddr, EndpointAddress dstAddr) { MessageElement sender = message.getMessageElement(GOSSIP_NAMESPACE, GOSSIP_SENDER_ELEMENT_NAME); MessageElement text = message.getMessageElement(GOSSIP_NAMESPACE, GOSSIP_GOSSIP_ELEMENT_NAME); // Make sure that the message contains the required elements. if ((null == sender) || (null == text)) { System.err.println("Someone sent us an incomplete message."); return; } // Check if the message is from ourself and should be ignored. if (!showOwn && group.getPeerID().toString().equals(sender.toString())) { // It's from ourself and we are configured to ignore it. return; } // Print the message's gossip text along with who it's from. System.out.println(sender.toString() + " says : " + text.toString()); } /** * Send a gossip message using the endpoint propagate method. */ public void sendGossip() { try { EndpointService currentEndpoint = endpoint; if (null == currentEndpoint) { return; } // Create a new message. Message gossipMessage = new Message(); // Add a "sender" element containing our peer id. MessageElement sender = new StringMessageElement(GOSSIP_SENDER_ELEMENT_NAME, group.getPeerID().toString(), null); gossipMessage.addMessageElement(GOSSIP_NAMESPACE, sender); // Add a "gossip" element containing our gossip text. MessageElement text = new StringMessageElement(GOSSIP_GOSSIP_ELEMENT_NAME, gossip, null); gossipMessage.addMessageElement(GOSSIP_NAMESPACE, text); // Send the message to the network using endpoint propagation. currentEndpoint.propagate(gossipMessage, getAssignedID().toString(), null); } catch (IOException ex) { Logger.getLogger(GossipService.class.getName()).log(Level.SEVERE, "Failed sending gossip message.", ex); } } }