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

oracle.kv.impl.util.registry.RegistryUtils Maven / Gradle / Ivy

/*-
 * Copyright (C) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This file was distributed by Oracle as part of a version of Oracle NoSQL
 * Database made available at:
 *
 * http://www.oracle.com/technetwork/database/database-technologies/nosqldb/downloads/index.html
 *
 * Please see the LICENSE file included in the top-level directory of the
 * appropriate version of Oracle NoSQL Database for a copy of the license and
 * additional information.
 */

package oracle.kv.impl.util.registry;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.rmi.AccessException;
import java.rmi.AlreadyBoundException;
import java.rmi.ConnectException;
import java.rmi.ConnectIOException;
import java.rmi.NotBoundException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.ExportException;
import java.rmi.server.RMIClientSocketFactory;
import java.rmi.server.RMISocketFactory;
import java.rmi.server.RemoteObject;
import java.rmi.server.RemoteRef;
import java.rmi.server.UnicastRemoteObject;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.net.ssl.SSLHandshakeException;

import oracle.kv.AuthenticationFailureException;
import oracle.kv.KVStoreConfig;
import oracle.kv.impl.admin.CommandService;
import oracle.kv.impl.admin.CommandServiceAPI;
import oracle.kv.impl.admin.param.GlobalParams;
import oracle.kv.impl.admin.param.Parameters;
import oracle.kv.impl.admin.param.StorageNodeParams;
import oracle.kv.impl.api.RequestHandler;
import oracle.kv.impl.api.RequestHandlerAPI;
import oracle.kv.impl.arb.admin.ArbNodeAdmin;
import oracle.kv.impl.arb.admin.ArbNodeAdminAPI;
import oracle.kv.impl.client.admin.ClientAdminService;
import oracle.kv.impl.client.admin.ClientAdminServiceAPI;
import oracle.kv.impl.fault.OperationFaultException;
import oracle.kv.impl.monitor.MonitorAgent;
import oracle.kv.impl.monitor.MonitorAgentAPI;
import oracle.kv.impl.rep.admin.RepNodeAdmin;
import oracle.kv.impl.rep.admin.RepNodeAdminAPI;
import oracle.kv.impl.security.login.LoginHandle;
import oracle.kv.impl.security.login.LoginManager;
import oracle.kv.impl.security.login.TrustedLogin;
import oracle.kv.impl.security.login.TrustedLoginAPI;
import oracle.kv.impl.security.login.UserLogin;
import oracle.kv.impl.security.login.UserLoginAPI;
import oracle.kv.impl.sna.StorageNodeAgentAPI;
import oracle.kv.impl.sna.StorageNodeAgentInterface;
import oracle.kv.impl.test.RemoteTestAPI;
import oracle.kv.impl.test.RemoteTestInterface;
import oracle.kv.impl.topo.ArbNode;
import oracle.kv.impl.topo.ArbNodeId;
import oracle.kv.impl.topo.RepGroupId;
import oracle.kv.impl.topo.RepNode;
import oracle.kv.impl.topo.RepNodeId;
import oracle.kv.impl.topo.ResourceId;
import oracle.kv.impl.topo.ResourceId.ResourceType;
import oracle.kv.impl.topo.StorageNode;
import oracle.kv.impl.topo.StorageNodeId;
import oracle.kv.impl.topo.Topology;
import oracle.kv.impl.util.HostPort;
import oracle.kv.impl.util.registry.RMISocketPolicy.SocketFactoryArgs;
import oracle.kv.impl.util.registry.ssl.SSLClientSocketFactory;

/**
 * RegistryUtils encapsulates the operations around registry lookups and
 * registry bindings. It's responsible for producing RMI binding names for the
 * various remote interfaces used by the store, and wrapping them in an
 * appropriate API class.
 * 

* All KV store registry binding names are of the form: *

* { kvstore name } : { component full name } : * [ main | monitor | admin | login | trusted_login ] *

* This class is responsible for the construction of these names. Examples: * FooStore:sn1:monitor names the monitor interface of the SNA. * Foostore:rg5-rn12:admin names the admin interface of the RN rg5-rn2. * * In future we may make provisions for making a version be a part of the name, * e.g. FooStore:sn1:monitor:5, for version 5 of the SNA monitor interface. *

* Note that all exception handling is done by the invokers of the get/rebind * methods. * * @see VersionedRemote */ public class RegistryUtils { /** * The exception message used in a ConnectIOException that is thrown when * there may be a security mismatch between the client and the server. */ public static final String POSSIBLE_SECURITY_MISMATCH_MESSAGE = "Problem connecting to the storage node agent, which may be caused" + " by a security mismatch between the client and server, or by" + " another network connectivity issue"; /** * The overriding registry CSF used on the client side. Synchronize on * the class when accessing this field. */ private static ClientSocketFactory registryCSF = null; /** * The map of kvstore name to registry ClientSocketFactory. Synchronize * on the class when accessing this field. */ private static final Map storeToRegistryCSFMap = new ConcurrentHashMap(); /** * A regular expression used to parse the port number from the string * representation of remote object, which may contain the endpoint info: * endpoint:[host:port<,RMIServerSocketFactory><,RMIClientSocketFactory>] * where the info enclosed with <> are optional */ private static final Pattern PARSE_PORT_REGEX = Pattern.compile("endpoint:\\[.*:(\\d{1,5})[,\\]]"); /* The topology that is the basis for generation of binding names. */ private final Topology topology; /* The login manager to be used for initializing APIs */ private final LoginManager loginMgr; /* Used to separate the different name components */ private static final String BINDING_NAME_SEPARATOR = ":"; /* * The different interface types associated with a remotely accessed * topology component. The component, depending upon its function, may only * implement a subset of the interfaces listed below. */ public enum InterfaceType { MAIN, /* The main interface. */ MONITOR, /* The monitoring interface. */ ADMIN, /* The administration interface. */ TEST, /* The test interface. */ LOGIN, /* The standard login interface */ TRUSTED_LOGIN; /* The trusted login interface */ public String interfaceName() { return name().toLowerCase(); } } /** * The constructor * * @param topology the topology used as the basis for operations * @param loginMgr a null-allowable LoginManager to be used for * authenticating RMI access. When accessing a non-secure instance * or when executing only operations that do not require authentication, * this may be null. */ public RegistryUtils(Topology topology, LoginManager loginMgr) { super(); this.topology = topology; this.loginMgr = loginMgr; } /** Returns the topology supplied to the constructor. */ public Topology getTopology() { return topology; } /** Returns the login manager supplied to the constructor. */ public LoginManager getLoginManager() { return loginMgr; } /** * Returns the API wrapper for the RMI stub associated with request handler * for the RN identified by repNodeId. If the RN is not * present in the topology null is returned. */ public RequestHandlerAPI getRequestHandler(RepNodeId repNodeId) throws RemoteException, NotBoundException { final RepNode repNode = topology.get(repNodeId); return (repNode == null) ? null : RequestHandlerAPI.wrap ((RequestHandler) lookup(repNodeId.getFullName(), InterfaceType.MAIN, repNode.getStorageNodeId())); } /** * Returns the API wrapper for the RMI stub associated with the * administration of the RN identified by repNodeId. */ public RepNodeAdminAPI getRepNodeAdmin(RepNodeId repNodeId) throws RemoteException, NotBoundException { final RepNode repNode = topology.get(repNodeId); return RepNodeAdminAPI.wrap ((RepNodeAdmin) lookup(repNodeId.getFullName(), InterfaceType.ADMIN, repNode.getStorageNodeId()), getLogin(repNodeId)); } /** * A RepNodeAdmin interface will look like this: * storename:rg1-rn1:ADMIN */ public static boolean isRepNodeAdmin(String serviceName) { if (serviceName.toLowerCase().indexOf("rg") > 0) { return (serviceName.endsWith (InterfaceType.ADMIN.toString())); } return false; } /** * Returns the API wrapper for the RMI stub associated with the * login of the RN identified by repNodeId. */ public UserLoginAPI getRepNodeLogin(RepNodeId repNodeId) throws RemoteException, NotBoundException { final RepNode repNode = topology.get(repNodeId); final LoginHandle loginHdl = getLogin(repNodeId); return UserLoginAPI.wrap ((UserLogin) lookup(repNodeId.getFullName(), InterfaceType.LOGIN, repNode.getStorageNodeId()), loginHdl); } /** * Check whether the service name appears to be a valid RepNode UserLogin * interface for some kvstore. * @return the store name from the RN login entry if this appears * to be a valid rep node login and null otherwise. */ public static String isRepNodeLogin(String serviceName) { /* * A RepNode UserLogin interface will look like this: * storename:rg1-rn1:LOGIN */ final int firstSep = serviceName.indexOf(BINDING_NAME_SEPARATOR); if (firstSep < 0) { return null; } if (serviceName.toLowerCase().indexOf( RepGroupId.getPrefix(), firstSep) > 0 && serviceName.endsWith( BINDING_NAME_SEPARATOR + InterfaceType.LOGIN.toString())) { return serviceName.substring(0, firstSep); } return null; } /** * Check whether the service name appears to be a valid RepNode UserLogin * interface for the named kvstore. */ public static boolean isRepNodeLogin(String serviceName, String storeName) { return storeName.equals(isRepNodeLogin(serviceName)); } /** * Returns the API wrapper for the RMI stub associated with the test * interface of the RN identified by repNodeId. */ public RemoteTestAPI getRepNodeTest(RepNodeId repNodeId) throws RemoteException, NotBoundException { final RepNode repNode = topology.get(repNodeId); return RemoteTestAPI.wrap ((RemoteTestInterface) lookup(repNodeId.getFullName(), InterfaceType.TEST, repNode.getStorageNodeId())); } /** * Returns the API wrapper for the RMI stub associated with the monitor for * the service identified by resourceId. This should only be * called for components that support monitoring. An attempt to obtain an * agent for a non-monitorable component will result in an {@link * IllegalStateException}. */ public static MonitorAgentAPI getMonitor(String storeName, String snHostname, int snRegistryPort, ResourceId resourceId, LoginManager loginMgr) throws RemoteException, NotBoundException { return MonitorAgentAPI.wrap ((MonitorAgent) getInterface(storeName, snHostname, snRegistryPort, resourceId.getFullName(), InterfaceType.MONITOR), getLogin(loginMgr, snHostname, snRegistryPort, resourceId.getType())); } /** * Looks up the Admin (CommandService) in the registry without benefit of * the Topology. Used to bootstrap storage node registration. * @throws RemoteException * @throws NotBoundException */ public static CommandServiceAPI getAdmin(String hostname, int registryPort, LoginManager loginMgr) throws RemoteException, NotBoundException { final Registry registry = getRegistry(hostname, registryPort); return CommandServiceAPI.wrap ((CommandService) registry.lookup(GlobalParams.COMMAND_SERVICE_NAME), getLogin(loginMgr, hostname, registryPort, ResourceType.ADMIN)); } /** * Look up an Admin (CommandService) when a Topology is available. * @throws RemoteException * @throws NotBoundException */ public CommandServiceAPI getAdmin(StorageNodeId snid) throws RemoteException, NotBoundException { final Registry registry = getRegistry(snid); return CommandServiceAPI.wrap ((CommandService) registry.lookup(GlobalParams.COMMAND_SERVICE_NAME), getLogin(snid)); } public RemoteTestAPI getAdminTest(StorageNodeId snid) throws RemoteException, NotBoundException { final Registry registry = getRegistry(snid); return RemoteTestAPI.wrap ((RemoteTestInterface) registry.lookup(GlobalParams.COMMAND_SERVICE_TEST_NAME)); } public static RemoteTestAPI getAdminTest(String hostname, int registryPort) throws RemoteException, NotBoundException { final Registry registry = getRegistry(hostname, registryPort); return RemoteTestAPI.wrap ((RemoteTestInterface) registry.lookup(GlobalParams.COMMAND_SERVICE_TEST_NAME)); } public UserLoginAPI getAdminLogin(StorageNodeId snid) throws RemoteException, NotBoundException { final Registry registry = getRegistry(snid); return UserLoginAPI.wrap ((UserLogin) registry.lookup(GlobalParams.ADMIN_LOGIN_SERVICE_NAME), getLogin(snid)); } public static UserLoginAPI getAdminLogin(String hostname, int registryPort, LoginManager loginMgr) throws RemoteException, NotBoundException { final Registry registry = getRegistry(hostname, registryPort); return UserLoginAPI.wrap ((UserLogin) registry.lookup(GlobalParams.ADMIN_LOGIN_SERVICE_NAME), getLogin(loginMgr, hostname, registryPort, ResourceType.ADMIN)); } /** * Look up an ClientAdminService on the master Admin without benefit of * the Topology. Used to execute DDL operations. * @throws RemoteException * @throws NotBoundException */ public static ClientAdminServiceAPI getAdminDDL(String hostname, int registryPort, LoginManager loginMgr) throws RemoteException, NotBoundException { final Registry registry = getRegistry(hostname, registryPort); return ClientAdminServiceAPI.wrap ((ClientAdminService) registry.lookup(GlobalParams.CLIENT_ADMIN_SERVICE_NAME), getLogin(loginMgr, hostname, registryPort, ResourceType.ADMIN)); } /** * Look up an ClientAdminService on the master Admin when a Topology is * available. Used to execute DDL operations. * @throws RemoteException * @throws NotBoundException */ public ClientAdminServiceAPI getAdminDDL(StorageNodeId snid) throws RemoteException, NotBoundException { final Registry registry = getRegistry(snid); return ClientAdminServiceAPI.wrap ((ClientAdminService) registry.lookup(GlobalParams.CLIENT_ADMIN_SERVICE_NAME), getLogin(snid)); } /** * Returns an API wrapper for the RMI handle to the RepNodeAdmin. */ public static RepNodeAdminAPI getRepNodeAdmin(String storeName, String snHostname, int snRegistryPort, RepNodeId rnId, LoginManager loginMgr) throws RemoteException, NotBoundException { return RepNodeAdminAPI.wrap ((RepNodeAdmin) getInterface(storeName, snHostname, snRegistryPort, rnId.getFullName(), InterfaceType.ADMIN), getLogin(loginMgr, snHostname, snRegistryPort, ResourceType.REP_NODE)); } /** * Returns an API wrapper for the RMI handle to the ArbNodeAdmin. */ public static ArbNodeAdminAPI getArbNodeAdmin(String storeName, String snHostname, int snRegistryPort, ArbNodeId arbId, LoginManager loginMgr) throws RemoteException, NotBoundException { return ArbNodeAdminAPI.wrap ((ArbNodeAdmin) getInterface(storeName, snHostname, snRegistryPort, arbId.getFullName(), InterfaceType.ADMIN), getLogin(loginMgr, snHostname, snRegistryPort, ResourceType.ARB_NODE)); } /** * Returns an API wrapper for the RMI handle to the RepNode UserLogin. */ public static UserLoginAPI getRepNodeLogin(String storeName, String snHostname, int snRegistryPort, RepNodeId rnId, LoginManager loginMgr) throws RemoteException, NotBoundException { return getRepNodeLogin(storeName, snHostname, snRegistryPort, rnId.getFullName(), loginMgr); } /** * Returns an API wrapper for the RMI handle to the RepNode UserLogin. */ public static UserLoginAPI getRepNodeLogin(String storeName, String snHostname, int snRegistryPort, String rnFullName, LoginManager loginMgr) throws RemoteException, NotBoundException { return UserLoginAPI.wrap ((UserLogin) getInterface(storeName, snHostname, snRegistryPort, rnFullName, InterfaceType.LOGIN), getLogin(loginMgr, snHostname, snRegistryPort, ResourceType.REP_NODE)); } /** * If a startup problem is found for this resource, throw an exception with * the information, because that's the primary problem. */ public static void checkForStartupProblem(String storeName, String snHostname, int snRegistryPort, ResourceId rId, StorageNodeId snId, LoginManager loginMgr) { StringBuilder startupProblem = null; try { final StorageNodeAgentAPI sna = getStorageNodeAgent(storeName, snHostname, snRegistryPort, snId, loginMgr); startupProblem = sna.getStartupBuffer(rId); } catch (Exception secondaryProblem) { /* * We couldn't get to the SNA to check. Eat this secondary problem * so the first problem isn't hidden. */ } if (startupProblem != null) { throw new OperationFaultException("Problem starting process for " + rId + ":" + startupProblem.toString()); } } private static Remote getInterface(String storeName, String snHostname, int snRegistryPort, String rnName, InterfaceType interfaceType) throws RemoteException, NotBoundException { final Registry registry = getRegistry(snHostname, snRegistryPort, storeName); return registry.lookup(bindingName(storeName, rnName, interfaceType)); } /** * Looks up a StorageNode in the registry without benefit of the * Topology. Used to bootstrap storage node registration. * @throws RemoteException * @throws NotBoundException */ public static StorageNodeAgentAPI getStorageNodeAgent(String hostname, int registryPort, String serviceName, LoginManager loginMgr) throws RemoteException, NotBoundException { final Registry registry = getRegistry(hostname, registryPort); final StorageNodeAgentInterface snai = (StorageNodeAgentInterface) registry.lookup(serviceName); final LoginHandle loginHdl = getLogin(loginMgr, hostname, registryPort, ResourceType.STORAGE_NODE); return StorageNodeAgentAPI.wrap(snai, loginHdl); } public StorageNodeAgentAPI getStorageNodeAgent(StorageNodeId storageNodeId) throws RemoteException, NotBoundException { final StorageNodeAgentInterface snai = (StorageNodeAgentInterface) lookup(storageNodeId.getFullName(), InterfaceType.MAIN, storageNodeId); final LoginHandle loginHdl = getLogin(storageNodeId); return StorageNodeAgentAPI.wrap(snai, loginHdl); } /** * Returns the API wrapper for the RMI stub associated with the * administration of the ARB identified by arbNodeId. */ public ArbNodeAdminAPI getArbNodeAdmin(ArbNodeId arbNodeId) throws RemoteException, NotBoundException { final ArbNode arbNode = topology.get(arbNodeId); return ArbNodeAdminAPI.wrap ((ArbNodeAdmin) lookup(arbNodeId.getFullName(), InterfaceType.ADMIN, arbNode.getStorageNodeId()), getLogin(arbNodeId)); } /** * Returns the API wrapper for the RMI stub associated with the test * interface of the AN identified by arbNodeId. */ public RemoteTestAPI getArbNodeTest(ArbNodeId arbNodeId) throws RemoteException, NotBoundException { final ArbNode arbNode = topology.get(arbNodeId); return RemoteTestAPI.wrap ((RemoteTestInterface) lookup(arbNodeId.getFullName(), InterfaceType.TEST, arbNode.getStorageNodeId())); } public static RemoteTestAPI getStorageNodeAgentTest (String storeName, String hostname, int registryPort, StorageNodeId snid) throws RemoteException, NotBoundException { final Registry registry = getRegistry(hostname, registryPort); final String serviceName = bindingName(storeName, snid.getFullName(), InterfaceType.TEST); return RemoteTestAPI.wrap ((RemoteTestInterface) registry.lookup(serviceName)); } public RemoteTestAPI getStorageNodeAgentTest(StorageNodeId storageNodeId) throws RemoteException, NotBoundException { return RemoteTestAPI.wrap ((RemoteTestInterface) lookup(storageNodeId.getFullName(), InterfaceType.TEST, storageNodeId)); } /** * Get the SNA trusted login */ public static TrustedLoginAPI getStorageNodeAgentLogin(String hostname, int registryPort) throws RemoteException, NotBoundException { final Registry registry = getRegistry(hostname, registryPort); return TrustedLoginAPI.wrap ((TrustedLogin) registry.lookup( GlobalParams.SNA_LOGIN_SERVICE_NAME)); } /** * Checks whether the service name appears to be a valid StorageNodeAgent * TrustedLogin interface. */ public static boolean isStorageNodeAgentLogin(String serviceName) { /* * A StorageAgentNode TrustedLogin interface will look like this: * SNA:TRUSTED_LOGIN */ return serviceName.equals(GlobalParams.SNA_LOGIN_SERVICE_NAME); } /** * Get the non-bootstrap SNA interface without using Topology. */ public static StorageNodeAgentAPI getStorageNodeAgent(String storeName, String snHostname, int snRegistryPort, StorageNodeId snId, LoginManager loginMgr) throws RemoteException, NotBoundException { final StorageNodeAgentInterface snai = (StorageNodeAgentInterface) getInterface(storeName, snHostname, snRegistryPort, snId.getFullName(), InterfaceType.MAIN); final LoginHandle loginHdl = getLogin(loginMgr, snHostname, snRegistryPort, ResourceType.STORAGE_NODE); return StorageNodeAgentAPI.wrap(snai, loginHdl); } /** * Get the non-bootstrap SNA interface without using Topology. */ public static StorageNodeAgentAPI getStorageNodeAgent(String storename, StorageNode sn, LoginManager loginMgr) throws RemoteException, NotBoundException { return getStorageNodeAgent(storename, sn.getHostname(), sn.getRegistryPort(), sn.getResourceId(), loginMgr); } /** * Get the non-bootstrap SNA interface using the given Parameters object. */ public static StorageNodeAgentAPI getStorageNodeAgent(Parameters parameters, StorageNodeId snId, LoginManager loginMgr) throws RemoteException, NotBoundException { final String storename = parameters.getGlobalParams().getKVStoreName(); return getStorageNodeAgent(storename, parameters.get(snId), snId, loginMgr); } public static StorageNodeAgentAPI getStorageNodeAgent(String storename, StorageNodeParams snp, StorageNodeId snId, LoginManager loginMgr) throws RemoteException, NotBoundException { return getStorageNodeAgent(storename, snp.getHostname(), snp.getRegistryPort(), snId, loginMgr); } /** * Rebinds the RMI stub associated with request handler for the RN * identified by repNodeId. */ public void rebind(RepNodeId repNodeId, RequestHandler requestHandler) throws RemoteException { final RepNode repNode = topology.get(repNodeId); rebind(repNodeId.getFullName(), InterfaceType.MAIN, repNode.getStorageNodeId(), requestHandler); } /** * Rebinds the RMI stub associated with monitor agent for the RN identified * by repNodeId. */ public void rebind(RepNodeId repNodeId, MonitorAgent monitorAgent) throws RemoteException { final RepNode repNode = topology.get(repNodeId); rebind(repNodeId.getFullName(), InterfaceType.MONITOR, repNode.getStorageNodeId(), monitorAgent); } /** * Rebinds the RMI stub associated with administration of the RN * identified by repNodeId. */ public void rebind(RepNodeId repNodeId, RepNodeAdmin repNodeAdmin) throws RemoteException { final RepNode repNode = topology.get(repNodeId); rebind(repNodeId.getFullName(), InterfaceType.ADMIN, repNode.getStorageNodeId(), repNodeAdmin); } /** * Rebinds the object in the registry after assembling the binding name * from its base name and suffix if any. * * @param baseName the base name * @param interfaceType the suffix if any to be appended to the baseName * @param storageNodeId the storage node used to locate the registry * @param object the remote object to be bound * * @throws RemoteException */ private void rebind(String baseName, InterfaceType interfaceType, StorageNodeId storageNodeId, Remote object) throws RemoteException { exportAndRebind(bindingName(baseName, interfaceType), object, getRegistry(storageNodeId), null, null); } /** * Rebinds the object in the registry after assembling the binding name. * This form of rebind is used when a Topology object is not yet available. * * @param hostname the hostname associated with the registry * @param registryPort the registry port * @param storeName the name of the KV store * @param baseName the base name * @param interfaceType the suffix if any to be appended to the baseName * @param object the remote object to be bound * @param clientSocketFactory the client socket factory; a null value * results in use of RMI default timeout values * @param serverSocketFactory the server socket factory; a null value * results in use of RMI default bind values * * @throws RemoteException */ public static void rebind(String hostname, int registryPort, String storeName, String baseName, InterfaceType interfaceType, Remote object, ClientSocketFactory clientSocketFactory, ServerSocketFactory serverSocketFactory) throws RemoteException { rebind(hostname, registryPort, bindingName(storeName, baseName, interfaceType), object, clientSocketFactory, serverSocketFactory); } /** * Overloading only for rebinds where the default socket factories are * adequate. This may be in unit tests, or with KVS services that don't * need fine control over server connection backlogs or client timeouts. */ public static void rebind(String hostname, int registryPort, String storeName, String baseName, InterfaceType interfaceType, Remote object) throws RemoteException { rebind(hostname, registryPort, storeName, baseName, interfaceType, object, null, null); /* Use default socket factories. */ } /** * Rebinds the object in the registry using the specified service name. * This form of rebind is used by the StorageNodeAgent before it is * registered with a Topology. * * @param hostname the hostname associated with the registry * @param registryPort the registry port * @param serviceName * @param object the remote object to be bound * * @throws RemoteException */ public static void rebind(String hostname, int registryPort, String serviceName, Remote object, ClientSocketFactory clientSocketFactory, ServerSocketFactory serverSocketFactory) throws RemoteException { try { exportAndRebind(serviceName, object, getRegistry(hostname, registryPort), clientSocketFactory, serverSocketFactory); } catch (RemoteException re) { throw new RemoteException("Can't rebind " + serviceName + " at " + hostname + ":" + registryPort + " csf: " + clientSocketFactory + " ssf: " + serverSocketFactory, re); } } /** * Unbinds the object in the registry after assembling the binding name. It * also unexports the object after letting all active operations finish. * * @param hostname the hostname associated with the registry * @param registryPort the registry port * @param storeName the name of the KV store * @param baseName the base name * @param interfaceType the suffix if any to be appended to the baseName * @param object the object being unbound * * @return true if the registry had a binding and it was unbound,false if * the binding was not present in the registry * * @throws RemoteException */ public static boolean unbind(String hostname, int registryPort, String storeName, String baseName, InterfaceType interfaceType, Remote object) throws RemoteException { return unbind(hostname, registryPort, bindingName(storeName, baseName, interfaceType), object); } /** * Static unbind method for non-topology object. */ public static boolean unbind(String hostname, int registryPort, String serviceName, Remote object) throws RemoteException { final Registry registry = getRegistry(hostname, registryPort); try { registry.unbind(serviceName); /* Stop accepting new calls for this object. */ UnicastRemoteObject.unexportObject(object, true); return true; } catch (NotBoundException e) { return false; } } /** * Returns the binding name used in the registry. */ public static String bindingName(String storeName, String baseName, InterfaceType interfaceType) { return storeName + BINDING_NAME_SEPARATOR + baseName + BINDING_NAME_SEPARATOR + interfaceType; } /** * Initialize socket policies with default client socket factory timeout * settings for client utility use (e.g. admin client, ping, etc.). * * Whether to use SSL is determined from system properties. * TBD: the reference to system property usage could be invalidated by * pending proposed change to the security configuration API. */ public static void initRegistryCSF() { initRegistryCSF(KVStoreConfig.DEFAULT_REGISTRY_OPEN_TIMEOUT, KVStoreConfig.DEFAULT_REGISTRY_READ_TIMEOUT); } /** * Initialize socket policies with given client socket factory timeout * settings for client utility use (e.g. admin client, ping, etc.). * * Whether to use SSL is determined from system properties. * TBD: the reference to system property usage could be invalidated by * pending proposed change to the security configuration API. * * @param openTimeoutMs client socket factory open timeout * @param readTimeoutMs client socket factory read timeout */ public static void initRegistryCSF(int openTimeoutMs, int readTimeoutMs) { final RMISocketPolicy rmiPolicy = ClientSocketFactory.ensureRMISocketPolicy(); final String registryCsfName = ClientSocketFactory.registryFactoryName(); final SocketFactoryArgs args = new SocketFactoryArgs(). setCsfName(registryCsfName). setCsfConnectTimeout(openTimeoutMs). setCsfReadTimeout(readTimeoutMs); setRegistryCSF(rmiPolicy.getRegistryCSF(args), null); } /** * Set socket timeouts for ClientSocketFactory. * Only for KVStore client usage. * Whether to use SSL is determined from system properties, if not yet * configured. */ public static void setRegistrySocketTimeouts( int connectMs, int readMs, String storeName) { final RMISocketPolicy rmiPolicy = ClientSocketFactory.ensureRMISocketPolicy(); final String registryCsfName = ClientSocketFactory.registryFactoryName(); final SocketFactoryArgs args = new SocketFactoryArgs(). setKvStoreName(storeName). setCsfName(registryCsfName). setCsfConnectTimeout(connectMs). setCsfReadTimeout(readMs); setRegistryCSF(rmiPolicy.getRegistryCSF(args), storeName); } /** * Set the supplied CSF for use during registry lookups within the * KVStore server. */ public static void setServerRegistryCSF( ClientSocketFactory clientSocketFactory) { setRegistryCSF(clientSocketFactory, null); } /** * Set the supplied CSF for use during registry lookups, but except when * testing and not using SSL. If a storeName is specified, the CSF is * set as the default CSF AND for store-specific use. If storeName is null * it is only used as the default registry CSF. */ public static synchronized void setRegistryCSF (ClientSocketFactory clientSocketFactory, String storeName) { registryCSF = clientSocketFactory; if (storeName != null) { storeToRegistryCSFMap.put(storeName, clientSocketFactory); } } /* * Reset current JVM's static CSF setting, used by unit test */ public static synchronized void clearRegistryCSF() { registryCSF = null; SSLClientSocketFactory.clearUserSSLControlMap(); storeToRegistryCSFMap.clear(); } /** * Reset registry client socket factory after resetting the RMI socket * policy. These resets will allow future connections to take advantage of * updated truststore certificates, if any. */ public static synchronized void resetRegistryCSF(String storeName) { final RMISocketPolicy rmiPolicy = ClientSocketFactory.resetRMISocketPolicy(storeName); final ClientSocketFactory csf = storeToRegistryCSFMap.get(storeName); if (csf == null) { return; } /* * Using the same socket factory argument to reset client socket * factory */ final SocketFactoryArgs args = new SocketFactoryArgs(). setKvStoreName(storeName). setCsfName(csf.name). setCsfConnectTimeout(csf.getConnectTimeoutMs()). setCsfReadTimeout(csf.getReadTimeoutMs()); setRegistryCSF(rmiPolicy.getRegistryCSF(args), storeName); } /** * Common function to get a Registry reference using a pre-configured CSF * with an optional storeName as a CSF lookup key, for cases where multiple * stores are accessed concurrently. */ public static Registry getRegistry( String hostname, int port, String storeName) throws RemoteException { return new ExceptionWrappingRegistry( hostname, port, getRegistryCSF(storeName)); } static synchronized ClientSocketFactory getRegistryCSF(String storeName) { ClientSocketFactory csf = (storeName == null) ? null : storeToRegistryCSFMap.get(storeName); if (csf == null) { csf = registryCSF; } return csf; } /** * Convenience version of the method above, where the storeName is not * specified. This is suitable for all calls within a KVStore * component and for calls by applications that have no ability to access * multiple stores concurrently. */ public static Registry getRegistry(String hostname, int port) throws RemoteException { return getRegistry(hostname, port, null); } public static int findFreePort(int start, int end, String hostname) { ServerSocket serverSocket = null; for (int current = start; current <= end; current++) { try { /* * Try a couple different methods to be sure that the port is * truly available. */ serverSocket = new ServerSocket(current); serverSocket.close(); /** * Now using the hostname. */ serverSocket = new ServerSocket(); final InetSocketAddress sa = new InetSocketAddress(hostname, current); serverSocket.bind(sa); serverSocket.close(); return current; } catch (IOException e) /* CHECKSTYLE:OFF */ { /* Try the next port number. */ } /* CHECKSTYLE:ON */ } return 0; } /** * Get the service port on which the specified remote object is exported. * This method attempts to use a known format specified in PARSE_PORT_REGEX * to parse port number from the string that represents the remote object. * If the attempt fails, return 0. * * @param remote remote object * @return a positive port number on which the specified remote object is * exported, or 0 if the specified remote object is null or port number * cannot be retrieved */ public static int getServicePort(Remote remote) { if (remote == null) { return 0; } String str = remote.toString(); if (remote instanceof RemoteObject) { final RemoteRef remoteRef = ((RemoteObject) remote).getRef(); if (remoteRef != null) { str = remoteRef.remoteToString(); } } if (str != null) { final Matcher m = PARSE_PORT_REGEX.matcher(str); if (m.find()) { final String port = m.group(1); try { return Integer.parseInt(port); } catch (NumberFormatException ex) { return 0; } } } return 0; } /** * Log service name and port associated with the specified remote object. * This method attempts to use a known format to parse port number from the * string that represents the remote object. If the attempt fails, log the * string as it may contain useful info to figure out the port number. * * @param name the name to which the specified remote object is bound * @param remote remote object * @param logger logger */ public static void logServiceNameAndPort(String name, Remote remote, Logger logger) { if (logger == null || !logger.isLoggable(Level.INFO) || remote == null || name == null) { return; } final int port = getServicePort(remote); if (port > 0) { logger.info(name + " service port: " + port); } else { String str = remote.toString(); if (remote instanceof RemoteObject) { final RemoteRef remoteRef = ((RemoteObject) remote).getRef(); if (remoteRef != null) { str = remoteRef.remoteToString(); } } logger.info("Remote object: " + str + " is bound to " + name); } } /* Internal Utility methods. */ /** * Export the specified object and bind the resulting stub to the name in * the registry. This method takes special steps to leave out any * specified client socket factories when exporting the object so that * objects with unequal client socket factories can still be exported on * the same port. The client socket factories are then added back when * binding the client-side stub in the registry. * * @param name the name to bind * @param object the object to export * @param registry the registry * @param csf the client socket factory or null * @param ssf the server socket factory or null */ private static void exportAndRebind(String name, Remote object, Registry registry, ClientSocketFactory csf, ServerSocketFactory ssf) throws RemoteException { int port = 0; ServerSocket ss = null; if (ssf != null) { try { /* * Allow the ssf to determine in advance what server socket * should be used. This allows us to specify the port number * when exporting the object, which is necessary for RMI to * close the server socket when all objects exported on this * port are unexported. */ ss = ssf.preallocateServerSocket(); if (ss != null) { port = ss.getLocalPort(); } } catch (IOException ioe) { throw new ExportException( "Unable to create ServerSocket for export", ioe); } } final Remote remote; try { /* * Export the object using a ReplaceableRMIClientSocketFactory so * that the actual client socket factory does not prevent port * sharing. */ remote = UnicastRemoteObject.exportObject( object, port, ReplaceableRMIClientSocketFactory.INSTANCE, ssf); /* * The prepared server socket, if any, should have been consumed, * so null out ss to suppress the call to discardServerSocket * below. */ ss = null; } finally { /* If the export did not succeed, we may need to clean up */ if (ss != null && ssf != null) { ssf.discardServerSocket(ss); } } /* * Specify a replacement client socket factory when binding the stub in * the registry. The call will serialize the stub, which will cause * the desired replacement client socket factory to be substituted for * the temporary one. */ try { if (csf != null) { ReplaceableRMIClientSocketFactory.setReplacement(csf); } registry.rebind(name, remote); } catch (RemoteException re) { UnicastRemoteObject.unexportObject(object, true); throw re; } finally { if (csf != null) { ReplaceableRMIClientSocketFactory.setReplacement(null); } } if (ssf != null) { logServiceNameAndPort(name, remote, ssf.getConnectionLogger()); } } /** * Returns the binding name used in the registry. */ private String bindingName(String baseName, InterfaceType interfaceType) { return bindingName(topology.getKVStoreName(), baseName, interfaceType); } /** * Returns the registry associated with the storage node. */ public Registry getRegistry(StorageNodeId storageNodeId) throws RemoteException { final StorageNode storageNode = topology.get(storageNodeId); return getRegistry(storageNode.getHostname(), storageNode.getRegistryPort(), topology.getKVStoreName()); } /** * Looks up an object in the registry after assembling the binding name * from its base name and suffix if any. * * @param baseName the base name * @param interfaceType the suffix if any to be appended to the baseName * @param topology the topology used to resolve all resource ids * @param storageNodeId the storage node used to locate the registry * * @return the stub associated with the remote object * * @throws RemoteException * @throws NotBoundException */ private Remote lookup(String baseName, InterfaceType interfaceType, StorageNodeId storageNodeId) throws RemoteException, NotBoundException { final Registry registry = getRegistry(storageNodeId); return registry.lookup(bindingName(baseName, interfaceType)); } private LoginHandle getLogin(ResourceId resourceId) { if (loginMgr == null) { return null; } /* * Because not all LoginManagers can resolve topology, attempt to * resolve it here if possible. */ if (topology != null) { final Topology.Component comp = topology.get(resourceId); if (comp != null) { final StorageNodeId snId = comp.getStorageNodeId(); final StorageNode sn = topology.get(snId); if (sn != null) { return loginMgr.getHandle( new HostPort(sn.getHostname(), sn.getRegistryPort()), resourceId.getType()); } } } return loginMgr.getHandle(resourceId); } private static LoginHandle getLogin(LoginManager loginMgr, String hostname, int registryPort, ResourceType rtype) { if (loginMgr == null) { return null; } return loginMgr.getHandle(new HostPort(hostname, registryPort), rtype); } /** * A decorator class for Registry. When messages of some exception in * Registry need to be improved to show a clearer instruction, this class * gives a chance to do that. *

* Currently we wrap the {@link ConnectIOException} to tell users to check * the security configurations on client and server ends when this * exception is seen. */ private static class ExceptionWrappingRegistry implements Registry { private final String hostname; private final int registryPort; private final Registry registry; private ExceptionWrappingRegistry(String hostname, int registryPort, ClientSocketFactory csf) throws RemoteException { this.hostname = hostname; this.registryPort = registryPort; registry = LocateRegistry.getRegistry(hostname, registryPort, csf); } @Override public Remote lookup(String name) throws RemoteException, NotBoundException, AccessException { try { return registry.lookup(name); } catch (RemoteException re) { rethrow(re); } /* Unreachable code */ return null; } @Override public void bind(String name, Remote obj) throws RemoteException, AlreadyBoundException, AccessException { try { registry.bind(name, obj); } catch (RemoteException re) { rethrow(re); } } @Override public void unbind(String name) throws RemoteException, NotBoundException, AccessException { try { registry.unbind(name); } catch (RemoteException re) { rethrow(re); } } @Override public void rebind(final String name, final Remote obj) throws RemoteException, AccessException { try { registry.rebind(name, obj); } catch (RemoteException re) { rethrow(re); } } @Override public String[] list() throws RemoteException, AccessException { try { return registry.list(); } catch (RemoteException re) { rethrow(re); } /* Unreachable code */ return null; } /* * Note: Keep this method in sync with * AsyncRegistryUtils.TranslateExceptions.onResult and * AsyncRequestHandler.handleDialogException */ private void rethrow(RemoteException re) throws RemoteException { /* Wraps the CIOE to give a clearer message to users */ if (re instanceof ConnectIOException) { /* * If CIOE caused by the SSL handshake error, wraps as an * AuthenticationFailureException */ final Throwable cause = re.getCause(); if (cause instanceof SSLHandshakeException) { throw new AuthenticationFailureException(cause); } throw new ConnectIOException( POSSIBLE_SECURITY_MISMATCH_MESSAGE, re); } if (re instanceof ConnectException) { throw new ConnectException( "Unable to connect to the storage node agent at" + " host " + hostname + ", port " + registryPort + ", which may not be running", re); } throw re; } } /** * Define an RMI client socket factory that will replace itself with * another client factory during serialization, if one is specified, or * else with null, to use the default. All instances of this class are * equal to each other. Use this factory when exporting an RMI server so * that different client socket factories can be used on the client side * without requiring the use of a different server socket for each export. * *

If you want to use a non-standard client socket factory, call the * setReplacement method around an operation that serializes the * client-side stub, which will cause the instance of this class to be * replaced by the desired client-side client socket factory. [#24708] */ public static class ReplaceableRMIClientSocketFactory implements Serializable, RMIClientSocketFactory { /** Use this default instance since all instances are equal. */ public static final ReplaceableRMIClientSocketFactory INSTANCE = new ReplaceableRMIClientSocketFactory(); private static final long serialVersionUID = 1; /** * Stores the replacement client socket factory for the current thread. */ private static final ThreadLocal replacement = new ThreadLocal(); @Override public boolean equals(Object object) { return object instanceof ReplaceableRMIClientSocketFactory; } @Override public int hashCode() { return 424242; } /** * Call the default RMI socket factory if this instance has not been * replaced. */ @Override public Socket createSocket(String host, int port) throws IOException { final RMIClientSocketFactory defaultFactory = RMISocketFactory.getDefaultSocketFactory(); return defaultFactory.createSocket(host, port); } /** * Specify the client socket factory that should replace this instance * during serialization. Make sure to clear the setting to null after * serialization is complete, to avoid holding a reference to the * replacement. */ static void setReplacement(RMIClientSocketFactory csf) { replacement.set(csf); } /** * Replace this instance with the replacement, if not null. Otherwise, * replace this instance with null, which will in turn be replaced by * the default RMI socket factory when it is deserialized. */ private Object writeReplace() { final RMIClientSocketFactory csf = replacement.get(); if (csf != null) { return csf; } return null; } /** * Instances of this class should never be deserialized because they * are always replaced during serialization. */ @SuppressWarnings("unused") private void readObject(ObjectInputStream in) { throw new AssertionError(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy