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

org.glassfish.enterprise.iiop.impl.RoundRobinPolicy Maven / Gradle / Ivy

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2006-2013 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.
 */
// Portions Copyright 2017-2022 Payara Foundation and/or its affiliates
// Payara Foundation and/or its affiliates elects to include this software in this distribution under the GPL Version 2 license

package org.glassfish.enterprise.iiop.impl;

import com.sun.corba.ee.spi.folb.ClusterInstanceInfo;
import com.sun.corba.ee.spi.folb.SocketInfo;
import org.glassfish.internal.api.ORBLocator;
import org.glassfish.jndi.cosnaming.IiopUrl;
import org.glassfish.logging.annotation.LogMessageInfo;

import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.UnknownHostException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;

import static org.glassfish.api.naming.NamingClusterInfo.IC_BASED;
import static org.glassfish.api.naming.NamingClusterInfo.IC_BASED_WEIGHTED;
import static org.glassfish.api.naming.NamingClusterInfo.LOAD_BALANCING_PROPERTY;
import static org.glassfish.enterprise.iiop.impl.NamingClusterInfoImpl.logger;

/**
 * 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 will have the following structure: * *

 * {@code
 * - 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 machine hosting 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 the endpoints 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 be provided back. * Then on, whenever there is any change in the clustershape, * the listener will get the updated list of endpoints from the server. * 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: * *

 * {@code
 * 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: * *
 * {@code
 *    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 endpoints and * perform the above. * * @author Sheetal Vartak **/ public class RoundRobinPolicy { @LogMessageInfo(message = "Could not find an endpoint to send request to.") public static final String COULD_NOT_FIND_ENDPOINT = "AS-ORB-00004"; @LogMessageInfo(message = "Unknown host: {0} Exception thrown : {1}") public static final String UNKNOWN_HOST = "AS-ORB-00005"; @LogMessageInfo(message = "No Endpoints selected in com.sun.appserv.iiop.endpoints property. Using JNDI Provider URL {0} instead") public static final String NO_ENDPOINTS_SELECTED_PROVIDER = "AS-ORB-00006"; @LogMessageInfo(message = "Exception : {0} thrown for bad provider URL String: {1}") public static final String PROVIDER_EXCEPTION = "AS-ORB-00007"; // 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 SecureRandom rand = new SecureRandom(); private List endpointsList = new LinkedList(); private int totalWeight; private List resolvedEndpoints; private static final int default_weight = 10; // 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(LOAD_BALANCING_PROPERTY, IC_BASED); return policy.equals(IC_BASED_WEIGHTED); } 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) { if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "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) { if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "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 { logger.log(Level.FINE, "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) { // support IPV6 literal address String[] host_port = new String[2]; int i = str.lastIndexOf(':'); host_port[0] = str.substring(0, i); host_port[1] = str.substring(i + 1); 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); return new ClusterInstanceInfo(server_identifier, weight, sil); } /* * 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); logger.log(Level.WARNING, NO_ENDPOINTS_SELECTED_PROVIDER, providerURLString); return newList; } catch (MalformedURLException me) { logger.log(Level.WARNING, PROVIDER_EXCEPTION, new Object[] { me, 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); } if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "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; if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "getNextRotation: result={0}", instanceInfo); } return convertIntoCorbaloc(instanceInfo); } lowerLimit = upperLimit; // fineLog( "lowerLimit = {0}", lowerLimit); i++; } logger.log(Level.WARNING, COULD_NOT_FIND_ENDPOINT); 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(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 || addr instanceof Inet6Address) { 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) { logger.log(Level.WARNING, UNKNOWN_HOST, new Object[] { host, ukhe }); 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 synchronized List getHostPortList() { return resolvedEndpoints; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy