All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.jxta.impl.resolver.ResolverServiceImpl Maven / Gradle / Ivy

/*
 * 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 :
 *   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 JXTA Protocols Specification : Endpoint Resolver Protocol
 */
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 handlers = Collections.synchronizedMap(new HashMap(5));
    private final Map srdiHandlers = Collections.synchronizedMap(new HashMap(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 true 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!
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy