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

com.sun.enterprise.naming.impl.RoundRobinPolicy Maven / Gradle / Ivy

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2006-2011 Oracle and/or its affiliates. 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_1_1.html
 * or packager/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 packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [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 com.sun.enterprise.naming.impl;

import java.util.logging.Level;
import com.sun.logging.LogDomains;
import java.util.logging.Logger;

import java.util.LinkedList;
import java.util.List;
import java.net.InetAddress;

import java.net.MalformedURLException;
import java.net.UnknownHostException;

import com.sun.jndi.cosnaming.IiopUrl;
import com.sun.corba.ee.spi.folb.ClusterInstanceInfo;
import com.sun.corba.ee.spi.folb.SocketInfo;
import com.sun.enterprise.config.serverbeans.Cluster;
import java.net.Inet4Address;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.glassfish.internal.api.ORBLocator;

/**
 * The list of endpoints are randomized the very first time.
 *  This happens only once( when called from the static block
 * of SerialInitContextFactory class).
 * Simple RoundRobin is a special case of Weighted Round Robin where the
 * weight per endpoint is equal.With the dynamic reconfiguration 
 * implementation, the endpoints list willhave the following structure:
 * - server_identifier (a stringified name for the machine)
 * - weight- list of SocketInfo {type (type = CLEAR_TEXT or SSL) + 
 *         IP address + port }
 * The above structure supports multi-homed machines 
 * i.e. one machinehosting multiple IP addresses.
 * The RoundRobinPolicy class can be the class that is also implementing
 * the Listener interface for listening to events generated whenever there
 * is a change in the cluster shape. The listener/event design is still
 * under construction.This list of endpoints will have to be created during 
 * bootstrapping(i.e. when the client first starts up.) This list will comprise
 * of theendpoints specified by the user in "com.sun.appserv.iiop.endpoints"
 * property. We can assume a default weight for these endpoints (e.g 10).
 * This list will be used to make the first lookup call. During the first 
 * lookup call, the actual list of endpoints will beprovided back. 
 * Then on, whenever there is any change in the clustershape, 
 * the listener will get the updated list of endpoints from theserver.
 * The implementation for choosing the endpoint from the list of endpoints
 * is as follows:Let's assume 4 endpoints:A(wt=10), B(wt=30), C(wt=40), 
 * D(wt=20). 
 * Using the Random API, generate a random number between 1 and10+30+40+20.
 * Let's assume that the above list is randomized. Based on the weights, we
 * have intervals as follows:
 * 1-----10 (A's weight)
 * 11----40 (A's weight + B's weight)
 * 41----80 (A's weight + B's weight + C's weight)
 * 81----100(A's weight + B's weight + C's weight + C's weight)
 * Here's the psuedo code for deciding where to send the request:
 * if (random_number between 1 & 10) {send request to A;}
 * else if (random_number between 11 & 40) {send request to B;}
 * else if (random_number between 41 & 80) {send request to C;}
 * else if (random_number between 81 & 100) {send request to D;}
 * For simple Round Robin, we can assume the same weight for all endpointsand 
 * perform the above.
 * @author Sheetal Vartak
 * @date 8/2/05
 **/

public class RoundRobinPolicy {
    // Each SocketInfo.type() must either start with SSL, or be CLEAR_TEXT
    private static final String SSL = "SSL" ;
    private static final String CLEAR_TEXT = "CLEAR_TEXT" ;

    private static final Logger _logger = LogDomains.getLogger(
        RoundRobinPolicy.class, LogDomains.JNDI_LOGGER);

    private static java.util.Random rand = new java.util.Random();

    private List endpointsList = 
	 new LinkedList();

    private int totalWeight = 0;

    private List resolvedEndpoints ;

    private static final int default_weight = 10;

    private static void warnLog( String fmt, Object... args ) {
        doLog( Level.WARNING, fmt, args ) ;
    }

    private static void infoLog( String fmt, Object... args ) {
        doLog( Level.INFO, fmt, args ) ;
    }

    private static void fineLog( String fmt, Object... args ) {
        doLog( Level.FINE, fmt, args ) ;
    }

    private static void doLog( Level level, String fmt, Object... args ) {
        if (_logger.isLoggable( level )) {
            _logger.log(level, fmt, args );
        }
    }

    //called during bootstrapping
    public RoundRobinPolicy(List list) {
	setClusterInstanceInfoFromString(list);
    }

    // Copy list, changing any type that does not start with SSL to CLEAR_TEXT.
    private List filterSocketInfos( List sis ) {
        final List result = new ArrayList() ;
        for (SocketInfo si : sis) {
            final String newType = si.type().startsWith(SSL) ?
                si.type() : CLEAR_TEXT ;
            final SocketInfo siCopy = new SocketInfo( newType,
                si.host(), si.port() ) ;
            result.add( siCopy ) ;
        }
        return result ;
    }

    private boolean isWeighted() {
	String policy = System.getProperty(
            SerialInitContextFactory.LOAD_BALANCING_PROPERTY,
            SerialInitContextFactory.IC_BASED );
	if (!policy.equals(SerialInitContextFactory.IC_BASED)) {
	    warnLog("loadbalancing.polibeforecy.incorrect");
        }

	final boolean isw = policy.equals(
            SerialInitContextFactory.IC_BASED_WEIGHTED);
        return isw ;
    }

    private List filterClusterInfo(
        List info ) {

        boolean isw = isWeighted() ;
        ArrayList newList =
            new ArrayList() ;
        totalWeight = 0 ;

	for (ClusterInstanceInfo clinfo : info) {
            final int newWeight = isw ? clinfo.weight() : default_weight ;

            final List newEndpoints =
                filterSocketInfos( clinfo.endpoints() ) ;
            final ClusterInstanceInfo newClinfo = new ClusterInstanceInfo(
                clinfo.name(), newWeight, newEndpoints ) ;
            newList.add( newClinfo ) ;

	    totalWeight += newWeight ;
        }

        return newList ;
    }

    private boolean containsMatchingAddress( List list,
        String host, int port ) {

        for (ClusterInstanceInfo info : list) {
             for (SocketInfo si : info.endpoints()) {
                 if (si.type().equals( CLEAR_TEXT )) {
                     if (si.host().equals( host) && si.port() == port) {
                         return true ;
                     }
                 }
            }
        }

        return false ;
    }

    // Add those elements of the second list that do not contain a clear
    // text address that appears in the first list.
    private List merge( List first,
        List second ) {

        List result = new ArrayList() ;
        result.addAll( first ) ;
        for (ClusterInstanceInfo info : second) {
            for (SocketInfo si : info.endpoints()) {
                if (!containsMatchingAddress(first, si.host(), si.port() )) {
                    result.add( info ) ;
                }
            }
        }
        return result ;
    }

    private List fromHostPortStrings( List list ) {
        List result =
            new LinkedList  ();

        for( String elem : list) {
            ClusterInstanceInfo info = makeClusterInstanceInfo( elem,
                default_weight ) ;
            result.add( info );
        }

        return result ;
    }


    //will be called after dynamic reconfig
    // used in GroupInfoServiceObserverImpl
    synchronized final void setClusterInstanceInfo(
        List list) {
        fineLog( "setClusterInstanceInfo: list={0}", list ) ;

        List filtered = filterClusterInfo(list) ;
        List resolved = fromHostPortStrings( resolvedEndpoints ) ;

        endpointsList = merge( filtered, resolved ) ;
    }

    // Note: regard any addresses supplied here as a permanent part of the
    // cluster.
    synchronized final void setClusterInstanceInfoFromString(
        List list) {
        fineLog( "setClusterInstanceInfoFromString: list={0}", list ) ;

        List newList = list;
	if (newList.isEmpty()) {
	    newList = getEndpointForProviderURL(
                System.getProperty(ORBLocator.JNDI_PROVIDER_URL_PROPERTY));
	}

	//randomize the list before adding it to linked list
	if (!newList.isEmpty()) {
	    List newList2 = randomize(newList);
            resolvedEndpoints = new ArrayList( newList2 ) ;
	    endpointsList = fromHostPortStrings(newList2) ;
            // Put in a default for total weight; any update will correct this.
            totalWeight = 10*endpointsList.size() ;
	} else {
	    fineLog( "no.endpoints" );
	}
    }   

    /**
     * during bootstrapping, weight is assumed "10" for all endpoints
     * then on, whenever server sends updates list,
     * create the list again here with right weights
     */
    private ClusterInstanceInfo makeClusterInstanceInfo(String str, 
        int weight) {

	String[] host_port = str.split(":");
	String server_identifier = ""; //for bootstrapping, can be ""
	String type = CLEAR_TEXT; //will be clear_text for bootstrapping
	SocketInfo socketInfo = new SocketInfo(
            type, host_port[0], Integer.parseInt( host_port[1]) );
        List sil = new ArrayList(1) ;
        sil.add( socketInfo ) ;

	ClusterInstanceInfo instanceInfo = new ClusterInstanceInfo(
            server_identifier, weight, sil ) ;
	return instanceInfo;
    }

    /*
     * This method checks for other ways of specifying endpoints
     * namely JNDI provider url 
     * orb host:port is used only if even env passed into 
     * getInitialContext is empty. This check is performed in 
     * SerialInitContextFactory.getInitialContext()
     */
    public List getEndpointForProviderURL(String providerURLString) {
	if (providerURLString != null) {
	    try {
		final IiopUrl providerURL = new IiopUrl(providerURLString);
		final List newList = getAddressPortList(providerURL);
		warnLog( "no.endpoints.selected.provider", providerURLString );
                return newList ;
	    } catch (MalformedURLException me) {
		warnLog( "provider.exception", me.getMessage(),
                    providerURLString);
	    }	    
	} 
	return new ArrayList() ;
    }
    
    /**
     * randomize the list.  Note: this empties its argument.
     */
    private List randomize( List list ) {
        List result = new ArrayList( list.size() ) ;
        while (!list.isEmpty()) {
            int random = rand.nextInt( list.size() ) ;
            String elem = list.remove( random ) ;
            result.add( elem ) ;
        }

        fineLog( "Randomized list {0}", result ) ;
        return result ;
    }

    /*
     * get a new shape of the endpoints
     * For e.g. if list contains A,B,C
     * if the logic below chooses B as the endpoint to send the req to
     * then return B,C,A.
     * logic used is as described in Class description comments
     */
    public synchronized List getNextRotation() {
	int lowerLimit = 0; //lowerLimit
	int random = 0;
	//make sure that the random # is not 0
	//Random API gives a number between 0 and sumOfAllWeights
	//But our range intervals are from 1-upperLimit, 
	//11-upperLimit and so
	//on. Hence we dont want random # to be 0.
        // fineLog( "RoundRobinPolicy.getNextRotation -> sumOfAllWeights = {0}",
            // totalWeight);
	while( random == 0) {
	    random = rand.nextInt(totalWeight);
	    if ( random != 0) {
		break;
	    }
	}
        // fineLog( "getNextRotation : random # = {0} sum of all weights = {1}",
            // new Object[]{random, totalWeight});
	int i = 0;
	for (ClusterInstanceInfo endpoint : endpointsList) {
	    int upperLimit = lowerLimit + endpoint.weight();
            // fineLog( "upperLimit = {0}", upperLimit);
	    if (random > lowerLimit && random <= upperLimit) {
		List instanceInfo = 
		    new LinkedList();
		
		//add the sublist at index 0 
		instanceInfo.addAll(0, 
                    endpointsList.subList(i, endpointsList.size()));

		//add the remaining list
		instanceInfo.addAll(endpointsList.subList(0, i));

                endpointsList = instanceInfo ;

		//print the contents...
		fineLog( "getNextRotation: result={0}",
                    instanceInfo.toString());
		
		return convertIntoCorbaloc(instanceInfo);
	    }
	    lowerLimit = upperLimit;
	    // fineLog( "lowerLimit = {0}", lowerLimit);
	    i++;    
	}
	warnLog("Could not find an endpoint to send request to!");
	return new ArrayList() ;
    }
    
    private List convertIntoCorbaloc(List list) {
	List host_port = new ArrayList();
	for (ClusterInstanceInfo endpoint : list) {
	    List sinfos = endpoint.endpoints();
            for (SocketInfo si : sinfos ) {
                // XXX this needs to be revised if we ever do a secure
                // bootstrap protocol for the initial corbaloc URL resolution
                if (si.type().equals( CLEAR_TEXT )) {
                    String element = si.host().trim() + ":" + si.port() ;
                    if (!host_port.contains( element )) {
                        host_port.add( element ) ;
                    }
                }
            }
	}
	return host_port ;
    }

    /**
     * following methods (over-loaded) for getting all IP addresses
     * corresponding to a particular host.
     * (multi-homed hosts).
     */
    private List getAddressPortList(List hostPortList) {
        // The list is assumed to contain : values
        List addressPortVector = new ArrayList();
        for (String str : hostPortList) {
            try {
                IiopUrl url = new IiopUrl("iiop://" + str);
                List apList = getAddressPortList(url);
                addressPortVector.addAll(apList);
            } catch (MalformedURLException me) {
                warnLog( "bad.host.port", str, me.getMessage() );
            }
        }

        return addressPortVector ;
    }
    
    private List getAddressPortList(IiopUrl iiopUrl) {
        // Pull out the host name and port
        IiopUrl.Address iiopUrlAddress = 
                (IiopUrl.Address)(iiopUrl.getAddresses().elementAt(0));
        String host = iiopUrlAddress.host;
        int portNumber = iiopUrlAddress.port;
        String port = Integer.toString(portNumber);
        // We return a list of : values
        return getAddressPortList(host, port);        
    }
    
    public List getAddressPortList(String host, String port) {
        // Get the ip addresses corresponding to the host.
        // XXX this currently does NOT support IPv6.
        try {
            InetAddress [] addresses = InetAddress.getAllByName(host);
            List addrs = new ArrayList() ;
            for (InetAddress addr : addresses) {
                if (addr instanceof Inet4Address) {
                    addrs.add( addr ) ;
                }
            }

            List ret = new ArrayList() ;
            for (InetAddress addr : addrs) {
                ret.add( addr.getHostAddress() + ":" + port ) ;
            }

            // We return a list of : values
            return ret;
        } catch (UnknownHostException ukhe) {
            warnLog( "unknown.host", host, ukhe.getMessage() );
            return new ArrayList() ;
        }
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder() ;
        sb.append( "RoundRobinPolicy[") ;
        boolean first = true ;
        for (ClusterInstanceInfo endpoint : endpointsList ) {
            if (first) {
                first = false ;
            } else {
                sb.append( ' ' ) ;
            }

            sb.append( endpoint.toString() ) ;
        }
        sb.append( ']' ) ;
        return sb.toString() ;
    }

    public List getHostPortList() {
        return resolvedEndpoints ;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy