Package org.jvnet.glassfish.comms.clb.core.common.chr

Source Code of org.jvnet.glassfish.comms.clb.core.common.chr.ConsistentHashRouter$ReadOnlySipFactory

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
* Copyright (c) Ericsson AB, 2004-2008. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License").  You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code.  If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license."  If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above.  However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package org.jvnet.glassfish.comms.clb.core.common.chr;

import com.ericsson.ssa.sip.SipFactoryImpl;
import com.ericsson.ssa.sip.dns.DnsResolver;

import org.glassfish.comms.api.datacentric.DcrPlugin;

import org.jvnet.glassfish.comms.clb.core.CLBConstants;
import org.jvnet.glassfish.comms.clb.core.ConsistentHashRequest;
import org.jvnet.glassfish.comms.clb.core.Controller;
import org.jvnet.glassfish.comms.clb.core.DCRFileUpdateEventListener;
import org.jvnet.glassfish.comms.clb.core.Router;
import org.jvnet.glassfish.comms.clb.core.ServerCluster;
import org.jvnet.glassfish.comms.clb.core.ServerInstance;
import org.jvnet.glassfish.comms.clb.core.common.chr.DcrPluginLoader.DcrPluginLoaderException;
import org.jvnet.glassfish.comms.clb.core.common.chr.dcr.DcrConfigurableHashKeyExtractor;
import org.jvnet.glassfish.comms.clb.core.common.chr.dcr.DcrRulesException;
import org.jvnet.glassfish.comms.clb.core.common.chr.dcr.DcrUtils;
import org.jvnet.glassfish.comms.clb.core.util.ConsistentHash;
import org.jvnet.glassfish.comms.clb.proxy.http.util.HttpRequest;
import org.jvnet.glassfish.comms.util.LogUtil;

import java.io.File;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.sip.Address;
import javax.servlet.sip.AuthInfo;
import javax.servlet.sip.Parameterable;
import javax.servlet.sip.ServletParseException;
import javax.servlet.sip.SipApplicationSession;
import javax.servlet.sip.SipFactory;
import javax.servlet.sip.SipServletRequest;
import javax.servlet.sip.SipURI;
import javax.servlet.sip.URI;
import org.jvnet.glassfish.comms.clb.core.util.LoadFactorTask;
import org.jvnet.glassfish.comms.clb.core.util.LoadFactorManager;
import org.jvnet.glassfish.comms.clb.core.util.LoadFactorChangeEventListener;


/**
* This is a router (common for HTTP and SIP) that implements the logic for
* server instance selection based on DCR. It can be used both for cluster (then
* it shall be set up by calling {@link #setClusterRouter(boolean)} with the
* argument true) and for request groups (default).
* <p>
* Server selection is performed in several steps depending of configuration:
*
* <pre>
* Extract hash key from request using configurable hash key extractor
* IF NOT configured as a cluster router:
*   First select server using a consistent hash which includes all servers in all
*     clusters (and assumes that they are all healthy): the &quot;Consistent Hash for
*     the Ideal Configuration&quot;
*   Check that selected server is healthy, and if it is use it.
*   Otherwise (if the selected server is faulty), select server using the &quot;Cluster
*   Router&quot; of the cluster of the failed server
* ENDIF
* If no server was found above, select server using a router which contains only
* the healthy servers of all clusters: the &quot;Consistent hash for the Actual
* Configuration&quot;
* Finally, if no servers were found at all return null.
* </pre>
*/
public class ConsistentHashRouter implements Router,
        DCRFileUpdateEventListener, LoadFactorChangeEventListener {
    /** Parameter used to setup consistent hashes */
    private static final int POINTS_PER_NODE = 1024;
    private static Logger logger = LogUtil.CLB_LOGGER.getLogger();

    /**
     * Consistent hash for the ideal configuration (not updated at server
     * failure). Contains all servers in all clusters.
     */
    protected ConsistentHash<String, ServerInstance> idealConsistentHash;

    /**
     * Consistent hash for the ideal configuration (updated at server
     * failure/recovery). Contains all healthy servers in all clusters.
     */
    private ConsistentHash<String, ServerInstance> actualConsistentHash;

    /**
     * All clusters that are associated with this router and that are used for
     * routing at fail-over.
     */
    private List<ServerCluster> associatedClusters = new ArrayList<ServerCluster>();

    /** The composite hash key extractor. */
    private CompositeHashKeyExtractor hashKeyExtractor = new CompositeHashKeyExtractor();

    /** The XML file for DCR configuration. */
    private File dcrFile;
    protected boolean requireIdealHash;
    private Controller controller;
    private HashMap<String, ServerInstance> instancesMap;
    private static final LoadFactorManager repository =
            LoadFactorManager.getInstance();

    /**
     * @param associatedClusters
     */
    public ConsistentHashRouter(List<ServerCluster> associatedClusters, boolean activeRouting) {
        this.associatedClusters = associatedClusters;

        //No need to maintain ideal hash for active routing
        if (!activeRouting) {
            requireIdealHash = true;
        } else {
            requireIdealHash = false;
        }

        //requireIdealHash = !activeRouting;
        instancesMap = new HashMap<String, ServerInstance>();
    }

    /**
     * <b>Note</b>, this method assumes that the specified request is an
     * instance of {@link ConsistentHashRequest}.
     * <p>
     * Also note that the provided request will be updated, the hash key will be
     * inserted into it.
     *
     * @see org.jvnet.glassfish.comms.clb.core.Router#selectInstance(Request)
     */
    public ServerInstance selectInstance(ConsistentHashRequest request) {
        // Must be a ConsistentHashRequest
        if (request.getHashKey() == null) {
            request.setHashKey(hashKeyExtractor.getHashKey(request));
        }

        // Then do routing based on the hash key...
        return getServerInstance(request);
    }

    /**
     * Gets the server instance for the specified request.
     *
     * @param req the request
     * @return the server instance for the given hash key
     */
    protected ServerInstance getServerInstance(ConsistentHashRequest req) {
        return getActiveInstance(req);
    }

    protected ServerInstance getActiveInstance(ConsistentHashRequest req) {
        if (req.getHashKey() == null) {
            return null;
        }

        ServerInstance server;
        server = actualConsistentHash.get(req.getHashKey());

        return server;
    }

    /**
     * Initializes this router.
     *
     * @see org.jvnet.glassfish.comms.clb.core.Router#initialize()
     */
    public boolean initialize() {
       
        repository.setProperties(controller.getProperties());
       
        setupHashKeyExtractor();

        if (requireIdealHash) {
            // Only create this if it is a request group router
            idealConsistentHash = new ConsistentHash<String, ServerInstance>(POINTS_PER_NODE, "CLB_IdealHash");
        }

        actualConsistentHash = new ConsistentHash<String, ServerInstance>(POINTS_PER_NODE, "CLB_ActualHash");

        for (ServerCluster cluster : associatedClusters) {
            for (ServerInstance server : cluster.getAllInstances()) {               
                instancesMap.put(server.toString(), server);
               
                server.addRouter(this);

                if (requireIdealHash) {
                    idealConsistentHash.addNode(server, false);
                }

                if (server.isHealthy() && server.isEnabled()) {
                    LoadFactorTask task = repository.getAddTask(server.toString());
                    if(task == null){
                        actualConsistentHash.addNode(server, false);
                    }else{
                        task.addListener(this);                       
                        actualConsistentHash.addNode(server,
                                task.getCurrFactor(), false);
                        addToRecoveringInstance(server);
                    }
                }
            }
        }

        if (requireIdealHash) {
            idealConsistentHash.setup();
        }

        actualConsistentHash.setup();

        return true;
    }

    /**
     * Sets the DCR file.
     *
     * @param dcrFile the DCR file
     */
    public void setDcrFile(File dcrFile) {
        this.dcrFile = dcrFile;
    }

    /**
     * Sets up the hash key extractor.
     * <p>
     * Pushes first a default hash key extractor and then a DCR extractor.
     */
    protected void setupHashKeyExtractor() {
        // First push the default hash key extractor...
        DefaultHashKeyExtractor defaultHashKeyExtractor = new DefaultHashKeyExtractor();
        pushHashKeyExtractor(defaultHashKeyExtractor);

        // Then DCR hash key extractor...
        DcrUtils.setup(DnsResolver.getInstance(), SipFactoryImpl.getInstance(), DnsResolver.getInstance());

        // Only try to read DCR file if it exists; otherwise fall-back to Call-ID, from-tag (the default extractor)
        if ((dcrFile != null) && dcrFile.exists()) {
            // Either the file is a DCR-XML or a java-plugin
            if (DcrPluginLoader.isSupported(dcrFile)) {
                try {
                    DcrPluginLoader dcrPluginLoader = new DcrPluginLoader(dcrFile);
                    final DcrPlugin dcrPlugin = dcrPluginLoader.loadDcrPlugin();

                    if (dcrPlugin != null) {
                        pushHashKeyExtractor(new DcrPluginHashKeyExtractor(dcrPlugin));

                        logger.log(Level.INFO, "clb.success_load_dcr_plugin", dcrFile);
                    } else {
                        logger.log(Level.WARNING, "clb.failed_load_dcr_plugin_null", dcrFile);
                    }
                } catch (DcrPluginLoaderException e) {
                    logger.log(Level.WARNING, "clb.failed_create_dcrplugin",
                            e.getMessage());
                    if(logger.isLoggable(Level.FINE)){
                        logger.log(Level.FINE, "clb.caught_an_exception", e);
                    }
                }
            } else {
                if (dcrFile.getName().toLowerCase().indexOf(".xml") >= 0) {
                    try {
                        DcrConfigurableHashKeyExtractor dcrHashKeyExtractor = new DcrConfigurableHashKeyExtractor(dcrFile.getAbsolutePath());
                        pushHashKeyExtractor(dcrHashKeyExtractor);
                    } catch (DcrRulesException e) {
                        logger.log(Level.WARNING, "clb.failed_read_dcrxml",
                                e.getMessage());
                        if(logger.isLoggable(Level.FINE)){
                            logger.log(Level.FINE, "clb.caught_an_exception", e);
                        }
                    }
                } else {
                    logger.log(Level.WARNING,
                            "clb.failed_load_dcr_plugin_unsupported", dcrFile);
                }
            }
        }

        pushHashKeyExtractor(new StickyHashKeyExtractor());
    }

    /**
     * Pushes the specified hash key extractor on top of the others.
     *
     * @param hke the hash key extractor
     */
    protected void pushHashKeyExtractor(HashKeyExtractor hke) {
        hashKeyExtractor.push(hke);
    }

    public void handleDisableEvent(ServerInstance instance) {
        if (instance.isHealthy()) {
            removeFromActualConsistentHash(instance);
        }
    }

    public void handleEnableEvent(ServerInstance instance) {
        if (instance.isHealthy()) {
            addToActualConsistentHash(instance);
        }
    }

    public void handleFailureEvent(ServerInstance instance,
            boolean isClusterShutdown) {
        if (!isClusterShutdown && instance.isEnabled()) {
            removeFromActualConsistentHash(instance);
        }
    }

    public void handleRecoveryEvent(ServerInstance instance,
            boolean isClusterStartup) {
        if (instance.isEnabled()) {
            if (!isClusterStartup){
                if(createAddTask(instance)){
                    return;
                }
            }
            addToActualConsistentHash(instance);
        }
    }

    private void addToActualConsistentHash(final ServerInstance instance) {
        if (logger.isLoggable(Level.FINER)) {
            logger.log(Level.FINER, "clb.adding_to_active_list", new Object[] { instance.getName() });
        }

        actualConsistentHash.addNode(instance, true);
    }

    private void removeFromActualConsistentHash(final ServerInstance instance) {
        if (logger.isLoggable(Level.FINER)) {
            logger.log(Level.FINER, "clb.removing_from_active_list", new Object[] { instance.getName() });
        }

        cancelAddTask(instance);
        actualConsistentHash.removeNode(instance, true);
    }

    public ServerInstance selectInstance(HttpRequest req) {
        throw new UnsupportedOperationException("Not applicable for Consistent hash router.");
    }

    public void processDCRFileUpdateEvent() {
        setDcrFile(new File(controller.getDCRFileName()));
        setupHashKeyExtractor();
    }

    public void setController(Controller controller) {
        this.controller = controller;

        String dcrFileName = controller.getDCRFileName();
        File dcrFile = null;

        if (dcrFileName != null) {
            dcrFile = new File(dcrFileName);
        }

        setDcrFile(dcrFile);

        if (controller != null) {
            controller.getDCRFileUpdateEventNotifier().addDCRFileUpdateEventListener(this);
        }
    }

    public void cleanup() {
        actualConsistentHash = null;
        associatedClusters = null;
        controller = null;
        dcrFile = null;
        hashKeyExtractor = null;
        idealConsistentHash = null;

        Iterator<ServerInstance> iter = instancesMap.values().iterator();
        while (iter.hasNext()) {
            cancelAddTask(iter.next());
        }
        instancesMap = null;
    }

    public boolean createAddTask(ServerInstance instance) {
        LoadFactorTask task = repository.createAddTask(instance.toString());
        if(task == null){
            return false;
        }
        task.addListener(this);
        addToRecoveringInstance(instance);
        return true;
    }

    private void cancelAddTask(ServerInstance instance) {
        LoadFactorTask task = repository.getAddTask(instance.toString());
        if(task != null){
            task.removeListener(this);
        }
        removeFromRecoveringInstance(instance);
    }

    protected void addToRecoveringInstance(ServerInstance serverInstance) {
    }

    protected void removeFromRecoveringInstance(ServerInstance serverInstance) {
    }

    public void updateLoadFactor(String instanceName,
            double currFactor){
        ServerInstance instance = instancesMap.get(instanceName);
        if(instance != null){
            actualConsistentHash.addNode(instance, currFactor, true);
        }
        if(currFactor >= 1.0){
            removeFromRecoveringInstance(instance);
        }
    }
           
    private static final class DcrPluginHashKeyExtractor implements HashKeyExtractor {
        private final DcrPlugin dcrPlugin;

        private DcrPluginHashKeyExtractor(DcrPlugin dcrPlugin) {
            this.dcrPlugin = dcrPlugin;
        }

        public String getHashKey(ConsistentHashRequest request) {
            if(request.isHttp()){
                return getHashKey(request.getHttpRequest());
            }
            return getHashKey(request.getSipRequest());
        }
       
        public String getHashKey(HttpRequest request) {
            return dcrPlugin.getKey(new HttpRequestAdapter(request), ReadOnlySipFactory.getInstance(), DnsResolver.getInstance(), DnsResolver.getInstance());
        }

        public String getHashKey(SipServletRequest req) {
            return dcrPlugin.getKey(req, ReadOnlySipFactory.getInstance(), DnsResolver.getInstance(), DnsResolver.getInstance());
        }
    }

    private static class ReadOnlySipFactory implements SipFactory {
        private static ReadOnlySipFactory instance = new ReadOnlySipFactory();
        private SipFactoryImpl delegate = SipFactoryImpl.getInstance();

        private ReadOnlySipFactory() {}

        static ReadOnlySipFactory getInstance() {
            return instance;
        }
       
        public Address createAddress(String sipAddress) throws ServletParseException {
            return delegate.createAddress(sipAddress);
        }

        public Address createAddress(URI uri) {
            return delegate.createAddress(uri);
        }

        public Address createAddress(URI uri, String displayName) {
            return delegate.createAddress(uri, displayName);
        }

        public SipApplicationSession createApplicationSession() {
            throw new UnsupportedOperationException("Not supported in a DCR plugin!");
        }

        public SipApplicationSession createApplicationSessionByKey(String sipApplicationKey) {
            throw new UnsupportedOperationException("Not supported in a DCR plugin!");
        }

        public AuthInfo createAuthInfo() {
            return delegate.createAuthInfo();
        }

        public Parameterable createParameterable(String s) throws ServletParseException {
            return delegate.createParameterable(s);
        }

        public SipServletRequest createRequest(SipApplicationSession sipAppSession, String method, Address from, Address to) {
            throw new UnsupportedOperationException("Not supported in a DCR plugin!");
        }

        public SipServletRequest createRequest(SipApplicationSession sipAppSession, String method, URI from, URI to) {
            throw new UnsupportedOperationException("Not supported in a DCR plugin!");
        }

        public SipServletRequest createRequest(SipApplicationSession sipAppSession, String method, String from, String to) throws ServletParseException {
            throw new UnsupportedOperationException("Not supported in a DCR plugin!");
        }

        public SipServletRequest createRequest(SipServletRequest origRequest, boolean sameCallId) {
            throw new UnsupportedOperationException("Not supported in a DCR plugin!");
        }

        public SipURI createSipURI(String user, String host) {
            return delegate.createSipURI(user, host);
        }

        public URI createURI(String s) throws ServletParseException {
            return delegate.createURI(s);
        }
    }
}
TOP

Related Classes of org.jvnet.glassfish.comms.clb.core.common.chr.ConsistentHashRouter$ReadOnlySipFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
', 'auto'); ga('send', 'pageview');