/* * Copyright (c) 2001-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 net.jxta.impl.resolver; import net.jxta.credential.Credential; import net.jxta.document.Advertisement; import net.jxta.document.MimeMediaType; import net.jxta.document.StructuredDocument; import net.jxta.document.StructuredDocumentFactory; import net.jxta.document.XMLDocument; import net.jxta.endpoint.*; import net.jxta.id.ID; import net.jxta.id.IDFactory; import net.jxta.impl.endpoint.router.EndpointRouter; import net.jxta.impl.endpoint.router.RouteControl; import net.jxta.impl.meter.MonitorManager; import net.jxta.impl.protocol.ResolverQuery; import net.jxta.impl.protocol.ResolverResponse; import net.jxta.impl.protocol.ResolverSrdiMsgImpl; import net.jxta.impl.resolver.resolverMeter.QueryHandlerMeter; import net.jxta.impl.resolver.resolverMeter.ResolverMeter; import net.jxta.impl.resolver.resolverMeter.ResolverMeterBuildSettings; import net.jxta.impl.resolver.resolverMeter.ResolverServiceMonitor; import net.jxta.impl.resolver.resolverMeter.SrdiHandlerMeter; import net.jxta.logging.Logging; import net.jxta.membership.MembershipService; import net.jxta.meter.MonitorResources; import net.jxta.peer.PeerID; import net.jxta.peergroup.PeerGroup; import net.jxta.platform.Module; import net.jxta.protocol.ModuleImplAdvertisement; import net.jxta.protocol.ResolverQueryMsg; import net.jxta.protocol.ResolverResponseMsg; import net.jxta.protocol.ResolverSrdiMsg; import net.jxta.protocol.RouteAdvertisement; import net.jxta.rendezvous.RendezVousService; import net.jxta.rendezvous.RendezVousStatus; import net.jxta.resolver.QueryHandler; import net.jxta.resolver.ResolverService; import net.jxta.resolver.SrdiHandler; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; /** * Implements the {@link net.jxta.resolver.ResolverService} using the standard * JXTA Endpoint Resolver Protocol (ERP). * * @see net.jxta.resolver.ResolverService * @see <a href="http://spec.jxta.org/v1.0/docbook/JXTAProtocols.html#proto-erp">JXTA Protocols Specification : Endpoint Resolver Protocol</a> */ public class ResolverServiceImpl implements ResolverService { /** * Logger */ private final static transient Logger LOG = Logger.getLogger(ResolverServiceImpl.class.getName()); /** * Resolver query endpoint postfix */ public final static String outQueNameShort = "ORes"; /** * Resolver response endpoint postfix */ public final static String inQueNameShort = "IRes"; /** * Resolver srdi endpoint postfix */ public final static String srdiQueNameShort = "Srdi"; /** * MIME Type for gzipped SRDI messages. */ private final static MimeMediaType GZIP_MEDIA_TYPE = new MimeMediaType("application/gzip").intern(); private String outQueName = outQueNameShort; private String inQueName = inQueNameShort; private String srdiQueName = srdiQueNameShort; private String handlerName = null; private PeerGroup group = null; private ModuleImplAdvertisement implAdvertisement = null; private EndpointService endpoint = null; private MembershipService membership = null; private RouteControl routeControl = null; private final Map<String, QueryHandler> handlers = Collections.synchronizedMap(new HashMap<String, QueryHandler>(5)); private final Map<String, SrdiHandler> srdiHandlers = Collections.synchronizedMap(new HashMap<String, SrdiHandler>(5)); private EndpointListener queryListener = null; private EndpointListener responseListener = null; private EndpointListener srdiListener = null; private ResolverServiceMonitor resolverServiceMonitor; private ResolverMeter resolverMeter; /** * the resolver interface object */ private ResolverService resolverInterface = null; /** * Encapsulates current Membership Service credential. */ final static class CurrentCredential { /** * The current default credential */ final Credential credential; /** * The current default credential in serialized XML form. */ final XMLDocument credentialDoc; CurrentCredential(Credential credential, XMLDocument credentialDoc) { this.credential = credential; this.credentialDoc = credentialDoc; } } /** * The current Membership service default credential. */ CurrentCredential currentCredential; /** * Listener we use for membership property events. */ private class CredentialListener implements PropertyChangeListener { /** * Standard Constructor */ CredentialListener() { } /** * {@inheritDoc} */ public void propertyChange(PropertyChangeEvent evt) { if (MembershipService.DEFAULT_CREDENTIAL_PROPERTY.equals(evt.getPropertyName())) { if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) { LOG.fine("New default credential event"); } synchronized (ResolverServiceImpl.this) { Credential cred = (Credential) evt.getNewValue(); XMLDocument credentialDoc; if (null != cred) { try { credentialDoc = (XMLDocument) cred.getDocument(MimeMediaType.XMLUTF8); currentCredential = new CurrentCredential(cred, credentialDoc); } catch (Exception all) { if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.log(Level.WARNING, "Could not generate credential document", all); } currentCredential = null; } } else { currentCredential = null; } } } } } final CredentialListener membershipCredListener = new CredentialListener(); /** * Convenience method for constructing an endpoint address from an id * * @param destPeer peer id * @param serv the service name (if any) * @param parm the service param (if any) * @return endpointAddress for this peer id. */ private static EndpointAddress mkAddress(ID destPeer, String serv, String parm) { return new EndpointAddress("jxta", destPeer.getUniqueValue().toString(), serv, parm); } /** * {@inheritDoc} */ public void init(PeerGroup group, ID assignedID, Advertisement impl) { implAdvertisement = (ModuleImplAdvertisement) impl; this.group = group; handlerName = assignedID.toString(); String uniqueStr = group.getPeerGroupID().getUniqueValue().toString(); outQueName = uniqueStr + outQueNameShort; inQueName = uniqueStr + inQueNameShort; srdiQueName = uniqueStr + srdiQueNameShort; if (ResolverMeterBuildSettings.RESOLVER_METERING) { // Fix-Me: This needs to be moved to startApp() when the load order issue is resolved resolverServiceMonitor = (ResolverServiceMonitor) MonitorManager.getServiceMonitor(group, MonitorResources.resolverServiceMonitorClassID); if (resolverServiceMonitor != null) { resolverMeter = resolverServiceMonitor.getResolverMeter(); } } // Tell tell the world about our configuration. if (Logging.SHOW_CONFIG && LOG.isLoggable(Level.CONFIG)) { StringBuilder configInfo = new StringBuilder("Configuring Resolver Service : " + assignedID); if (implAdvertisement != null) { configInfo.append("\n\tImplementation :"); configInfo.append("\n\t\tModule Spec ID: ").append(implAdvertisement.getModuleSpecID()); configInfo.append("\n\t\tImpl Description : ").append(implAdvertisement.getDescription()); configInfo.append("\n\t\tImpl URI : ").append(implAdvertisement.getUri()); configInfo.append("\n\t\tImpl Code : ").append(implAdvertisement.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\tIn Queue name: ").append(outQueName); configInfo.append("\n\t\tOut Queue name: ").append(inQueName); configInfo.append("\n\t\tSRDI Queue name: ").append(srdiQueName); LOG.config(configInfo.toString()); } } /** * {@inheritDoc} */ public int startApp(String[] arg) { 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; } membership = group.getMembershipService(); if (null == membership) { if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.warning("Stalled until there is a membership service"); } return Module.START_AGAIN_STALLED; } // Register Listeners try { // Register Query Listener queryListener = new DemuxQuery(); if (!endpoint.addIncomingMessageListener(queryListener, handlerName, outQueName)) { if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) { LOG.severe("Cannot register listener (already registered)"); } } // Register Response Listener responseListener = new DemuxResponse(); if (!endpoint.addIncomingMessageListener(responseListener, handlerName, inQueName)) { if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) { LOG.severe("Cannot register listener (already registered)"); } } // Register SRDI Listener srdiListener = new DemuxSrdi(); if (!endpoint.addIncomingMessageListener(srdiListener, handlerName, srdiQueName)) { if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) { LOG.severe("Cannot register listener (already registered)"); } } } catch (Exception e) { if (Logging.SHOW_SEVERE && LOG.isLoggable(Level.SEVERE)) { LOG.log(Level.SEVERE, "failed to add listeners", e); } return -1; } synchronized (this) { // register our credential listener. membership.addPropertyChangeListener(MembershipService.DEFAULT_CREDENTIAL_PROPERTY, membershipCredListener); try { // set the initial version of the default credential. currentCredential = null; Credential credential = membership.getDefaultCredential(); XMLDocument credentialDoc; if (null != credential) { credentialDoc = (XMLDocument) credential.getDocument(MimeMediaType.XMLUTF8); currentCredential = new CurrentCredential(credential, credentialDoc); } } catch (Exception all) { if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.log(Level.WARNING, "could not get default credential", all); } } } return Module.START_OK; } /** * {@inheritDoc} */ public void stopApp() { endpoint.removeIncomingMessageListener(handlerName, outQueName); endpoint.removeIncomingMessageListener(handlerName, inQueName); if (null != srdiListener) { endpoint.removeIncomingMessageListener(handlerName, srdiQueName); } queryListener = null; responseListener = null; srdiListener = null; membership.removePropertyChangeListener("defaultCredential", membershipCredListener); currentCredential = null; routeControl = null; membership = null; group = null; } /** * {@inheritDoc} */ public synchronized ResolverService getInterface() { if (resolverInterface == null) { resolverInterface = new ResolverServiceInterface(this); } return resolverInterface; } /** * {@inheritDoc} */ public ModuleImplAdvertisement getImplAdvertisement() { return implAdvertisement; } /** * {@inheritDoc} */ public QueryHandler registerHandler(String name, QueryHandler handler) { if (ResolverMeterBuildSettings.RESOLVER_METERING && (resolverServiceMonitor != null)) { resolverServiceMonitor.registerQueryHandlerMeter(name); } return handlers.put(name, handler); } /** * {@inheritDoc} */ public QueryHandler unregisterHandler(String name) { if (ResolverMeterBuildSettings.RESOLVER_METERING && (resolverServiceMonitor != null)) { resolverServiceMonitor.unregisterQueryHandlerMeter(name); } return handlers.remove(name); } /** * given a name returns the query handler associated with it * * @param name the handler to lookup * @return returns the query handler */ public QueryHandler getHandler(String name) { return handlers.get(name); } /** * {@inheritDoc} */ public SrdiHandler registerSrdiHandler(String name, SrdiHandler handler) { if (ResolverMeterBuildSettings.RESOLVER_METERING && (resolverServiceMonitor != null)) { resolverServiceMonitor.registerSrdiHandlerMeter(name); } return srdiHandlers.put(name, handler); } /** * {@inheritDoc} */ public SrdiHandler unregisterSrdiHandler(String name) { if (ResolverMeterBuildSettings.RESOLVER_METERING && (resolverServiceMonitor != null)) { resolverServiceMonitor.unregisterSrdiHandlerMeter(name); } return srdiHandlers.remove(name); } /** * given a name returns the srdi handler associated with it * * @param name the handler to lookup * @return returns the SRDI handler */ public SrdiHandler getSrdiHandler(String name) { return srdiHandlers.get(name); } /** * {@inheritDoc} */ public void sendQuery(String destPeer, ResolverQueryMsg query) { if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) { LOG.fine("sending query to resolver handler: " + query.getHandlerName()); } // NOTE: Add route information about the issuing peer, so the // resolver query responding peer can respond to the issuer without // requiring any route discovery. In most case the responding peer // is unlikely to know the route to the query issuer. This is a good // optimization for edge peers. This optimzation is much less // important for RDV peers as they are more likely to have a route // to peers. Also, there is the concern that adding route info // in resolver query exchanged between RDV will increase overhead due // to the larger amount of information exchanged between RDV. // Only update query if the query does not already contain any route // information. We are mostly interested in the original src // route information. if (query.getSrcPeerRoute() == null) { if (getRouteControl() != null) { // FIXME tra 20031102 Until the new subscription service // is implemented, we use the Router Control IOCTL RouteAdvertisement route = routeControl.getMyLocalRoute(); if (route != null) { query.setSrcPeerRoute(route.clone()); } if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) { LOG.fine("Sending query with route info to " + destPeer); } } else { if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) { LOG.fine("No route control--could not set local route on query"); } } } String queryHandlerName = query.getHandlerName(); QueryHandlerMeter queryHandlerMeter = null; if (ResolverMeterBuildSettings.RESOLVER_METERING && (resolverServiceMonitor != null)) { queryHandlerMeter = resolverServiceMonitor.getQueryHandlerMeter(queryHandlerName); } if (destPeer == null) { try { Message queryMsg = new Message(); XMLDocument asDoc = (XMLDocument) query.getDocument(MimeMediaType.XMLUTF8); MessageElement docElem = new TextDocumentMessageElement(outQueName, asDoc, null); queryMsg.addMessageElement("jxta", docElem); RendezVousService rendezvous = group.getRendezVousService(); if (null != rendezvous) { if (rendezvous.getRendezVousStatus() != RendezVousStatus.ADHOC) { // Walk the message rendezvous.walk(queryMsg.clone(), handlerName, outQueName, RendezVousService.DEFAULT_TTL); } // propagate to local net as well rendezvous.propagateToNeighbors(queryMsg, handlerName, outQueName, 2); } else { endpoint.propagate(queryMsg, handlerName, outQueName); } if (ResolverMeterBuildSettings.RESOLVER_METERING && (queryHandlerMeter != null)) { queryHandlerMeter.querySentInGroup(query); } } catch (IOException e) { if (ResolverMeterBuildSettings.RESOLVER_METERING && (queryHandlerMeter != null)) { queryHandlerMeter.queryPropagateError(); } if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.log(Level.WARNING, "Failure during propagate", e); } } } else { // unicast instead boolean success = sendMessage(destPeer, null, handlerName, outQueName, outQueName, (XMLDocument) query.getDocument(MimeMediaType.XMLUTF8), false); if (ResolverMeterBuildSettings.RESOLVER_METERING && (queryHandlerMeter != null)) { if (success) { queryHandlerMeter.querySentViaUnicast(destPeer, query); } else { queryHandlerMeter.querySendError(); } } } } /** * {@inheritDoc} */ public void sendResponse(String destPeer, ResolverResponseMsg response) { if (destPeer == null) { propagateResponse(response); } else { QueryHandlerMeter queryHandlerMeter = null; try { if (ResolverMeterBuildSettings.RESOLVER_METERING && (resolverServiceMonitor != null)) { queryHandlerMeter = resolverServiceMonitor.getQueryHandlerMeter(response.getHandlerName()); } // Check if an optional route information is available to send the response RouteAdvertisement route = response.getSrcPeerRoute(); boolean success = sendMessage(destPeer, route, handlerName, inQueName, inQueName, (XMLDocument) response.getDocument(MimeMediaType.XMLUTF8), false); if (ResolverMeterBuildSettings.RESOLVER_METERING && (queryHandlerMeter != null)) { if (success) { queryHandlerMeter.responseSentViaUnicast(destPeer, response); } else { queryHandlerMeter.responseSendError(); } } } catch (Exception e) { if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.log(Level.WARNING, "Error in sending response", e); } if (ResolverMeterBuildSettings.RESOLVER_METERING && (queryHandlerMeter != null)) { queryHandlerMeter.responseSendError(); } } } } /** * {@inheritDoc} */ public void sendSrdi(String destPeer, ResolverSrdiMsg srdi) { String srdiHandlerName = srdi.getHandlerName(); SrdiHandlerMeter srdiHandlerMeter = null; if (ResolverMeterBuildSettings.RESOLVER_METERING && (resolverServiceMonitor != null)) { srdiHandlerMeter = resolverServiceMonitor.getSrdiHandlerMeter(srdiHandlerName); } if (destPeer == null) { RendezVousService rendezvous = group.getRendezVousService(); if (rendezvous == null) { // no rendezvous service, dump it. return; } Message propagateMsg = new Message(); try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); GZIPOutputStream gos = new GZIPOutputStream(baos); srdi.getDocument(MimeMediaType.XMLUTF8).sendToStream(gos); gos.finish(); gos.close(); byte gzipBytes[] = baos.toByteArray(); MessageElement zipElem = new ByteArrayMessageElement(srdiQueName, GZIP_MEDIA_TYPE, gzipBytes, null); propagateMsg.addMessageElement("jxta", zipElem); if (rendezvous.getRendezVousStatus() != RendezVousStatus.ADHOC) { rendezvous.walk(propagateMsg, handlerName, srdiQueName, RendezVousService.DEFAULT_TTL); } // propagate to local net as well rendezvous.propagateToNeighbors(propagateMsg, handlerName, srdiQueName, 2); if (ResolverMeterBuildSettings.RESOLVER_METERING && (srdiHandlerMeter != null)) { srdiHandlerMeter.messageSentViaWalker(srdi); } } catch (IOException e) { if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.log(Level.WARNING, "Failure sending srdi message", e); } if (ResolverMeterBuildSettings.RESOLVER_METERING && (srdiHandlerMeter != null)) { srdiHandlerMeter.errorPropagatingMessage(); } } } else { try { boolean success = sendMessage(destPeer, null, handlerName, srdiQueName, srdiQueName, (XMLDocument) srdi.getDocument(MimeMediaType.XMLUTF8), // compression true); if (ResolverMeterBuildSettings.RESOLVER_METERING && (srdiHandlerMeter != null)) { if (success) { srdiHandlerMeter.messageSentViaUnicast(destPeer, srdi); } else { srdiHandlerMeter.errorSendingMessage(); } } } catch (Exception e) { if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.log(Level.WARNING, "Error in sending srdi message", e); } if (ResolverMeterBuildSettings.RESOLVER_METERING && (srdiHandlerMeter != null)) { srdiHandlerMeter.errorSendingMessage(); } } } } private void repropagateQuery(Message msg, ResolverQueryMsg query) { RendezVousService rendezvous = group.getRendezVousService(); if ((null != rendezvous) && !group.isRendezvous()) { // not a Rendezvous peer? Let someone else forward it. return; } // just in case an excessive of attempt to forward a query // hopCount is used to determine forward counts independent of any other TTL if (query.getHopCount() > 3) { if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) { LOG.fine("discarding ResolverQuery. HopCount exceeded : " + query.getHopCount()); } if (ResolverMeterBuildSettings.RESOLVER_METERING && (resolverMeter != null)) { resolverMeter.propagationQueryDropped(query); } return; } XMLDocument asDoc = (XMLDocument) query.getDocument(MimeMediaType.XMLUTF8); MessageElement docElem = new TextDocumentMessageElement(outQueName, asDoc, null); msg.replaceMessageElement("jxta", docElem); // Re-propagate the message. // Loop and TTL control is done in demux and propagate(). The TTL // below is just a default it will be reduced appropriately. try { if (null != rendezvous) { if (rendezvous.getRendezVousStatus() != RendezVousStatus.ADHOC) { rendezvous.walk(msg, handlerName, outQueName, RendezVousService.DEFAULT_TTL); } // propagate to local net as well rendezvous.propagateToNeighbors(msg, handlerName, outQueName, 2); } else { endpoint.propagate(msg, handlerName, inQueName); } if (ResolverMeterBuildSettings.RESOLVER_METERING && (resolverMeter != null)) { resolverMeter.queryPropagatedViaWalker(query); } } catch (IOException e) { if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.log(Level.WARNING, "Failure propagating query", e); if (ResolverMeterBuildSettings.RESOLVER_METERING && (resolverMeter != null)) { resolverMeter.queryPropagationError(query); } } } } /** * Process a resolver query. * * @param query The query. * @param srcAddr Who sent the query to us. May not be the same as the * query originator. * @return the query id assigned to the query */ private int processQuery(ResolverQueryMsg query, EndpointAddress srcAddr) { String queryHandlerName = query.getHandlerName(); QueryHandler theHandler = getHandler(queryHandlerName); if (query.getHopCount() > 2) { if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) { LOG.fine("Discarding query #" + query.getQueryId() + " hopCount > 2 : " + query.getHopCount()); } // query has been forwarded too many times stop if (ResolverMeterBuildSettings.RESOLVER_METERING && (resolverServiceMonitor != null)) { QueryHandlerMeter queryHandlerMeter = resolverServiceMonitor.getQueryHandlerMeter(queryHandlerName); if (queryHandlerMeter != null) { queryHandlerMeter.queryHopCountDropped(); } else { resolverMeter.invalidQueryDiscarded(srcAddr); } } return ResolverService.OK; } if (theHandler == null) { if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) { LOG.fine("Discarding query #" + query.getQueryId() + ", no handler for :" + queryHandlerName); } // If this peer is a rendezvous peer, it needs to repropagate the query to other rendezvous peer that // may have a handler. if (ResolverMeterBuildSettings.RESOLVER_METERING && (resolverMeter != null)) { resolverMeter.unknownHandlerForQuery(query); } return ResolverService.Repropagate; } if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) { LOG.fine("Handing query #" + query.getQueryId() + " to : " + queryHandlerName); } QueryHandlerMeter queryHandlerMeter = null; long startTime = 0; if (ResolverMeterBuildSettings.RESOLVER_METERING && (resolverServiceMonitor != null)) { startTime = System.currentTimeMillis(); queryHandlerMeter = resolverServiceMonitor.getQueryHandlerMeter(queryHandlerName); } try { int result; if (theHandler instanceof InternalQueryHandler) { result = ((InternalQueryHandler) theHandler).processQuery(query, srcAddr); } else { result = theHandler.processQuery(query); } if (ResolverMeterBuildSettings.RESOLVER_METERING && (queryHandlerMeter != null)) { queryHandlerMeter.queryProcessed(query, result, System.currentTimeMillis() - startTime); } return result; } catch (Throwable any) { if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.log(Level.WARNING, "Uncaught Throwable from handler for : " + queryHandlerName, any); } if (ResolverMeterBuildSettings.RESOLVER_METERING && (queryHandlerMeter != null)) { queryHandlerMeter.errorWhileProcessingQuery(query); } // stop repropagation return ResolverService.OK; } } /** * Process a resolver response. * * @param resp The response. * @param srcAddr Who sent the response. May not be the same as the * originator response. */ private void processResponse(ResolverResponseMsg resp, EndpointAddress srcAddr) { String handlerName = resp.getHandlerName(); if (handlerName == null) { if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.warning("Missing handlername in response"); } if (ResolverMeterBuildSettings.RESOLVER_METERING && (resolverMeter != null)) { resolverMeter.invalidResponseDiscarded(srcAddr); } return; } QueryHandler theHandler = getHandler(handlerName); if (theHandler == null) { if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.warning("No handler for :" + handlerName); } if (ResolverMeterBuildSettings.RESOLVER_METERING && (resolverMeter != null)) { resolverMeter.unknownHandlerForResponse(srcAddr, resp); } return; } if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) { LOG.fine("Process response to query #" + resp.getQueryId() + " with " + handlerName); } QueryHandlerMeter queryHandlerMeter = null; long startTime = 0; if (ResolverMeterBuildSettings.RESOLVER_METERING && (resolverServiceMonitor != null)) { startTime = System.currentTimeMillis(); queryHandlerMeter = resolverServiceMonitor.getQueryHandlerMeter(handlerName); } try { if (theHandler instanceof InternalQueryHandler) { ((InternalQueryHandler) theHandler).processResponse(resp, srcAddr); } else { theHandler.processResponse(resp); } if (ResolverMeterBuildSettings.RESOLVER_METERING && (queryHandlerMeter != null)) { queryHandlerMeter.responseProcessed(resp, System.currentTimeMillis() - startTime, srcAddr); } } catch (Throwable all) { if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.log(Level.WARNING, "Uncaught Throwable from handler for: " + handlerName, all); } if (ResolverMeterBuildSettings.RESOLVER_METERING && (queryHandlerMeter != null)) { queryHandlerMeter.errorWhileProcessingResponse(srcAddr); } } } /** * propagate a response * * @param response response message to propagate */ private void propagateResponse(ResolverResponseMsg response) { Message propagateMsg = new Message(); String queryHandlerName = response.getHandlerName(); QueryHandlerMeter queryHandlerMeter = null; try { if (ResolverMeterBuildSettings.RESOLVER_METERING && (resolverServiceMonitor != null)) { queryHandlerMeter = resolverServiceMonitor.getQueryHandlerMeter(queryHandlerName); } XMLDocument responseDoc = (XMLDocument) response.getDocument(MimeMediaType.XMLUTF8); MessageElement elemDoc = new TextDocumentMessageElement(inQueName, responseDoc, null); propagateMsg.addMessageElement("jxta", elemDoc); RendezVousService rendezvous = group.getRendezVousService(); if (null != rendezvous) { rendezvous.walk(propagateMsg, handlerName, inQueName, RendezVousService.DEFAULT_TTL); if (ResolverMeterBuildSettings.RESOLVER_METERING && (queryHandlerMeter != null)) { queryHandlerMeter.responseSentViaWalker(response); } } else { endpoint.propagate(propagateMsg, handlerName, inQueName); if (ResolverMeterBuildSettings.RESOLVER_METERING && (queryHandlerMeter != null)) { // FIXME bondolo 20040909 not technically the correct metric queryHandlerMeter.responseSentViaWalker(response); } } } catch (IOException e) { if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.log(Level.WARNING, "failure during propagateResponse", e); } if (ResolverMeterBuildSettings.RESOLVER_METERING && (queryHandlerMeter != null)) { queryHandlerMeter.responsePropagateError(); } } } /** * Process an SRDI message. * * @param srdimsg The SRDI message. * @param srcAddr Who sent the message. May not be the same as the * originator of the message. */ private void processSrdi(ResolverSrdiMsgImpl srdimsg, EndpointAddress srcAddr) { String handlerName = srdimsg.getHandlerName(); if (handlerName == null) { if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.warning("Missing handlername in response"); } if (ResolverMeterBuildSettings.RESOLVER_METERING && (resolverMeter != null)) { resolverMeter.invalidSrdiMessageDiscarded(srcAddr); } return; } if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) { LOG.fine("Processing an SRDI msg for : " + handlerName + " in Group ID:" + group.getPeerGroupID()); } SrdiHandler theHandler = getSrdiHandler(handlerName); if (theHandler != null) { SrdiHandlerMeter srdiHandlerMeter = null; try { long startTime = 0; if (ResolverMeterBuildSettings.RESOLVER_METERING && (resolverServiceMonitor != null)) { startTime = System.currentTimeMillis(); srdiHandlerMeter = resolverServiceMonitor.getSrdiHandlerMeter(handlerName); } theHandler.processSrdi(srdimsg); if (ResolverMeterBuildSettings.RESOLVER_METERING && (srdiHandlerMeter != null)) { srdiHandlerMeter.messageProcessed(srdimsg, System.currentTimeMillis() - startTime, srcAddr); } } catch (Throwable all) { if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.log(Level.WARNING, "Uncaught Throwable from handler for: " + handlerName, all); } if (ResolverMeterBuildSettings.RESOLVER_METERING && (srdiHandlerMeter != null)) { srdiHandlerMeter.errorWhileProcessing(srcAddr); } } } else { if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING) && group.isRendezvous()) { LOG.warning("No srdi handler registered :" + handlerName + " for Group ID:" + group.getPeerGroupID()); } else if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) { LOG.fine("No srdi handler registered :" + handlerName + " for Group ID:" + group.getPeerGroupID()); } if (ResolverMeterBuildSettings.RESOLVER_METERING && (resolverMeter != null)) { resolverMeter.unknownHandlerForSrdiMessage(srcAddr, handlerName); } } } /** * Send a resolver message to a peer * * @param destPeer destination peer * @param route destination route advertisement * @param pName service name on the destination * @param pParam service param on the destination * @param tagName tag name of the message element * @param body the body of the message element * @param gzip If <code>true</code> then encode the message body using gzip. * @return {@code true} if successful */ private boolean sendMessage(String destPeer, RouteAdvertisement route, String pName, String pParam, String tagName, XMLDocument body, boolean gzip) { // Get the messenger ready ID dest; try { dest = IDFactory.fromURI(new URI(destPeer)); } catch (URISyntaxException badpeer) { if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.log(Level.WARNING, "bad destination peerid : " + destPeer, badpeer); } return false; } EndpointAddress destAddress = mkAddress(dest, pName, pParam); // FIXME add route to responses as well Messenger messenger = null; if (route == null) { if (Logging.SHOW_FINER && LOG.isLoggable(Level.FINER)) { LOG.finer("No route info available for " + destPeer); } } else { // ok we have a route let's pass it to the router if ((null == getRouteControl()) || (routeControl.addRoute(route) == RouteControl.FAILED)) { if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.warning("Failed to add route for " + route.getDestPeerID()); } } else { if (Logging.SHOW_FINER && LOG.isLoggable(Level.FINER)) { LOG.finer("Added route for " + route.getDestPeerID()); } } } if (Logging.SHOW_FINER && LOG.isLoggable(Level.FINER)) { LOG.finer("Creating a messenger immediate for :" + destAddress); } messenger = endpoint.getMessengerImmediate(destAddress, route); if (null == messenger) { if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) { LOG.fine("Failed creating messenger for " + destAddress); } return false; } Message msg = new Message(); try { MessageElement msgEl; if (gzip) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); GZIPOutputStream gos = new GZIPOutputStream(baos); body.sendToStream(gos); gos.finish(); gos.close(); byte gzipBytes[] = baos.toByteArray(); msgEl = new ByteArrayMessageElement(tagName, GZIP_MEDIA_TYPE, gzipBytes, null); } else { msgEl = new TextDocumentMessageElement(tagName, body, null); } msg.addMessageElement("jxta", msgEl); } catch (Exception ez1) { // Not much we can do if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.log(Level.WARNING, "Failed building message", ez1); } return false; } // Send the message if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) { LOG.fine("Sending " + msg + " to " + destAddress + " " + tagName); } // XXX 20040924 bondolo Convert this to ListenerAdaptor messenger.sendMessage(msg, null, null, new FailureListener(dest)); return true; } private RouteControl getRouteControl() { // Obtain the route control object to manipulate route information when sending and receiving resolver queries. if (routeControl == null) { // insignificant race condition here MessageTransport endpointRouter = endpoint.getMessageTransport("jxta"); if (endpointRouter != null) { routeControl = (RouteControl) endpointRouter.transportControl(EndpointRouter.GET_ROUTE_CONTROL, null); } else { if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.warning("Failed to get RouteControl object. Resolver will not set route hints."); } } } return routeControl; } /** * Inner class to handle incoming queries */ private class DemuxQuery implements EndpointListener { /** * {@inheritDoc} */ public void processIncomingMessage(Message message, EndpointAddress srcAddr, EndpointAddress dstAddr) { if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) { LOG.fine("Demuxing a query message from " + srcAddr); } MessageElement element = message.getMessageElement("jxta", outQueName); if (element == null) { if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.warning("Message does not contain a query. Discarding message"); } if (ResolverMeterBuildSettings.RESOLVER_METERING && (resolverMeter != null)) { resolverMeter.invalidQueryDiscarded(srcAddr); } return; } ResolverQueryMsg query; try { StructuredDocument asDoc = StructuredDocumentFactory.newStructuredDocument(element); query = new ResolverQuery(asDoc); } catch (IOException e) { if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.log(Level.WARNING, "Ill formatted resolver query, ignoring.", e); } if (ResolverMeterBuildSettings.RESOLVER_METERING && (resolverMeter != null)) { resolverMeter.invalidQueryDiscarded(srcAddr); } return; } catch (IllegalArgumentException e) { if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.log(Level.WARNING, "Ill formatted resolver query, ignoring.", e); } if (ResolverMeterBuildSettings.RESOLVER_METERING && (resolverMeter != null)) { resolverMeter.invalidQueryDiscarded(srcAddr); } return; } int res = processQuery(query, srcAddr); if (ResolverService.Repropagate == res) { if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) { LOG.fine("Repropagating query " + message + " from " + srcAddr); } repropagateQuery(message, query); } } } /** * Inner class to handle incoming responses */ private class DemuxResponse implements EndpointListener { /** * @inheritDoc */ public void processIncomingMessage(Message message, EndpointAddress srcAddr, EndpointAddress dstAddr) { if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) { LOG.fine("Demuxing a response from " + srcAddr); } MessageElement element = message.getMessageElement("jxta", inQueName); if (null == element) { if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) { LOG.fine("Message does not contain a response. Discarding message"); } if (ResolverMeterBuildSettings.RESOLVER_METERING && (resolverMeter != null)) { resolverMeter.invalidResponseDiscarded(srcAddr); } return; } ResolverResponse resolverResponse; try { StructuredDocument asDoc = StructuredDocumentFactory.newStructuredDocument(element); resolverResponse = new ResolverResponse(asDoc); } catch (IOException e) { if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.log(Level.WARNING, "Ill formatted resolver response, ignoring.", e); } if (ResolverMeterBuildSettings.RESOLVER_METERING && (resolverMeter != null)) { resolverMeter.invalidResponseDiscarded(srcAddr); } return; } catch (IllegalArgumentException e) { if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.log(Level.WARNING, "Ill formatted resolver response, ignoring.", e); } if (ResolverMeterBuildSettings.RESOLVER_METERING && (resolverMeter != null)) { resolverMeter.invalidResponseDiscarded(srcAddr); } return; } processResponse(resolverResponse, srcAddr); } } /** * Inner class to handle SRDI messages */ private class DemuxSrdi implements EndpointListener { /** * @inheritDoc */ public void processIncomingMessage(Message message, EndpointAddress srcAddr, EndpointAddress dstAddr) { if (Logging.SHOW_FINE && LOG.isLoggable(Level.FINE)) { LOG.fine("Demuxing an SRDI message from : " + srcAddr); } MessageElement element = message.getMessageElement("jxta", srdiQueName); if (element == null) { if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.warning("Message does not contain a SRDI element. Discarding message"); } if (ResolverMeterBuildSettings.RESOLVER_METERING && (resolverMeter != null)) { resolverMeter.invalidSrdiMessageDiscarded(srcAddr); } return; } ResolverSrdiMsgImpl srdimsg; try { if (element.getMimeType().getBaseMimeMediaType().equals(GZIP_MEDIA_TYPE)) { InputStream gzipStream = new GZIPInputStream(element.getStream()); StructuredDocument asDoc = StructuredDocumentFactory.newStructuredDocument(MimeMediaType.XMLUTF8, gzipStream); srdimsg = new ResolverSrdiMsgImpl(asDoc, membership); } else { StructuredDocument asDoc = StructuredDocumentFactory.newStructuredDocument(element); srdimsg = new ResolverSrdiMsgImpl(asDoc, membership); } } catch (IOException e) { if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.log(Level.WARNING, "Ill formatted SRDI message, ignoring.", e); } if (ResolverMeterBuildSettings.RESOLVER_METERING && (resolverMeter != null)) { resolverMeter.invalidSrdiMessageDiscarded(srcAddr); } return; } processSrdi(srdimsg, srcAddr); } } /** * Listener to find bad destinations and clean srdi tables for them. */ class FailureListener implements OutgoingMessageEventListener { final ID dest; FailureListener(ID dest) { this.dest = dest; } /** * {@inheritDoc} */ public void messageSendFailed(OutgoingMessageEvent event) { // Ignore the failure if it's a case of queue overflow. if (event.getFailure() == null) { return; } if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.warning("Clearing SRDI tables for failed peer : " + dest); } for (Object o : Arrays.asList(srdiHandlers.values().toArray())) { SrdiHandler theHandler = (SrdiHandler) o; try { theHandler.messageSendFailed((PeerID) dest, event); } catch (Throwable all) { if (Logging.SHOW_WARNING && LOG.isLoggable(Level.WARNING)) { LOG.log(Level.WARNING, "Uncaught Throwable from handler : " + theHandler, all); } } } } /** * {@inheritDoc} */ public void messageSendSucceeded(OutgoingMessageEvent event) {// great! } } }