/** * VMware Continuent Tungsten Replicator * Copyright (C) 2015 VMware, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Initial developer(s): Teemu Ollakka * Contributor(s): Robert Hodges, Edward Archibald, Gilles Rayrat, Ludovic Launer */ package com.continuent.tungsten.common.config.cluster; import java.text.MessageFormat; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.TreeMap; import org.apache.log4j.Logger; import com.continuent.tungsten.common.cluster.resource.ResourceType; import com.continuent.tungsten.common.config.TungstenProperties; /** * The SQL-SQLRouter can be configured either by properties stored in a set of * properties files, appropriately organized in a directory tree, or by a single * XML-based configuration file of the type managed by the Tungsten Manager. In * any case, either set of configuration information reduces to a set of * properties. Routers manage connections to a <cluster> and a <cluster> is * composed of a set of <datasource> which, in turn, have a set of properties of * their own. The directory structure is: resourceHome * <dataSourcename>.properties <dataSourcename>.properties */ public class RouterConfiguration extends ClusterConfiguration implements Cloneable { private static Logger logger = Logger.getLogger(RouterConfiguration.class); /** * */ @SuppressWarnings("unused") private static final long serialVersionUID = 1L; /** * RMI service name */ private String serviceName = ConfigurationConstants.TR_SERVICE_NAME; /** * RMI port */ private int port = new Integer( ConfigurationConstants.TR_RMI_PORT_DEFAULT) .intValue(); /** * RMI host */ private String host = ConfigurationConstants.TR_RMI_DEFAULT_HOST; /** * Indicates whether or not to enable the router on startup or to startup in * the disabled state. */ private boolean autoEnable = true; /** * Indicates whether or not to wait for active connections to disconnect * before disabling. */ private boolean waitForDisconnect = true; /** * Indicates the amount of time to wait for all connections to finish before * going out and forcibly closing them. */ private int waitForDisconnectTimeout = 0; /** * Indicates whether or not we'll wait for a particular type of resource to * become available if it is not already. */ private boolean waitIfUnavailable = true; /** * The amount of time to wait, if any, for a particular type of resource to * become available before throwing an exception. */ private int waitIfUnavailableTimeout = 0; private boolean waitIfDisabled = true; private int waitIfDisabledTimeout = 0; /** * The cluster member where the router is running. */ private String clusterMemberName; // /** // * If the property is non-null, this is a class that will be loaded tonull; // * listen for router notifications // */ // private String routerListenerClass = "com.continuent.tungsten.common.patterns.notification.adaptor.ResourceNotificationListenerStub"; private int notifyPort = 10121; private String notifierMonitorClass = "com.continuent.tungsten.common.patterns.notification.adaptor.ResourceNotifierStub"; private String dataSourceLoadBalancer_RO_RELAXED = "com.continuent.tungsten.router.resource.loadbalancer.RoundRobinSlaveLoadBalancer"; private boolean rrIncludeMaster = false; /** Router Gateway manager list */ private List<String> managerList = Arrays.asList("localhost"); /** Router gateway listen port */ private int routerGatewayPort = Integer .parseInt(ConfigurationConstants.TR_GW_PORT_DEFAULT); private String c3p0JMXUrl = "service:jmx:rmi:///jndi/rmi://localhost:3100/jmxrmi"; /** When disconnected from managers, time to wait before going "on hold" (not accepting new connections but letting current ones finish) */ private int delayBeforeOnHoldIfNoManager = ConfigurationConstants.DELAY_BEFORE_ONHOLD_IF_NO_MANAGER_DEFAULT; /** When disconnected from managers, time to wait before going offline */ private int delayBeforeOfflineIfNoManager = ConfigurationConstants.DELAY_BEFORE_OFFLINE_IF_NO_MANAGER_DEFAULT; /** * When disconnected from managers AND in maintenance mode, time to wait * before going offline */ private int delayBeforeOfflineInMaintenanceModeIfNoManager = ConfigurationConstants.DELAY_BEFORE_OFFLINE_IN_MAINTENANCE_MODE_IF_NO_MANAGER_DEFAULT; /** * Delay after which a manager connection is considered broken if no * keep-alive command was received. Make sure manager has * "manager.notifications.send" set to true and frequency is higher than * this value */ private int keepAliveTimeout = ConfigurationConstants.KEEP_ALIVE_TIMEOUT_DEFAULT; /** * When connecting to a manager, how long to wait for the connection to * succeed before trying the next manager in line. Default 5s, must be * positive and max 30s */ private int gatewayConnectTimeoutMs = ConfigurationConstants.GATEWAY_CONNECT_TIMEOUT_MS_DEFAULT; private boolean showRelativeLatency = false; private int routerClientThreadsPerService = 1; private int gatewayLocalBindStartingPort = 45847; /** * When reading manager commands in maintenance mode, the router will retry * a few times upon failure. This controls when to give up */ private long readCommandRetryTimeoutMs = ConfigurationConstants.READ_COMMAND_RETRY_TIMEOUT_MS_DEFAULT; public RouterConfiguration(String clusterName) throws ConfigurationException { super(clusterName); // set up the default service values setPort(new Integer(ConfigurationConstants.TR_RMI_PORT_DEFAULT)); setHost("localhost"); setServiceName(ConfigurationConstants.TR_SERVICE_NAME); } /** * Loads a router configuration from disk. * * @return a fully initialized router configuration * @throws ConfigurationException */ public RouterConfiguration load() throws ConfigurationException { load(ConfigurationConstants.TR_PROPERTIES); // TUC-1750 : managerList router properties is no longer in router.properties // Get the value from dataservices.properties DataServicesConfiguration d = DataServicesConfiguration.getInstance(); String managerList = d.getProps().get(props.get(ConfigurationConstants.CLUSTER_CLUSTERNAME)); props.put(ConfigurationConstants.CLUSTER_MANAGER_LIST, managerList); if (managerList==null) logger.warn((MessageFormat.format("Could not retrieve a value for {0} by reading {1}", ConfigurationConstants.CLUSTER_MANAGER_LIST, ConfigurationConstants.TR_PROPERTIES))); props.applyProperties(this, true); loadClusterDataSourceMap(); return this; } /** * Loads data cluster configurations from disk. * * @throws ConfigurationException */ public synchronized Map<String, Map<String, TungstenProperties>> loadClusterDataSourceMap() throws ConfigurationException { return loadClusterConfiguration(ResourceType.DATASOURCE); } /** * Returns all of the datasource configurations for a given cluster. * * @param clusterName * @return a map of TungstenProperties representing data sources for the * cluster */ public Map<String, TungstenProperties> getDataSourceMap(String clusterName) { Map<String, TungstenProperties> dsMap = new TreeMap<String, TungstenProperties>(); Map<String, Map<String, TungstenProperties>> clusterDataSourceMap; try { if ((clusterDataSourceMap = loadClusterDataSourceMap()) != null) { Map<String, TungstenProperties> foundMap = clusterDataSourceMap .get(clusterName); if (foundMap != null) { // Do some quick validation to make sure that // the datasource is well formed. for (TungstenProperties dsProps : foundMap.values()) { if (isValidDs(dsProps)) { dsMap.put(dsProps.getString("name"), dsProps); } } dsMap.putAll(foundMap); } } } catch (Exception e) { logger.error("Problem loading the datasource configuration", e); } return dsMap; } private boolean isValidDs(TungstenProperties dsToCheck) { String[] requiredProps = {"name", "vendor", "clusterName", "host", "driver", "url", "role", "precedence"}; for (String prop : requiredProps) { if (dsToCheck.getString(prop) == null) return false; } return true; } /** * Returns an existing datasource configuration, if there is one, for the * named dataService. * * @param clusterName * @param dsName * @return a TungstenProperties instances representing a data source * @throws ConfigurationException */ public TungstenProperties getDataSource(String clusterName, String dsName) throws ConfigurationException { TungstenProperties foundDs = getDataSourceMap(clusterName).get(dsName); if (foundDs == null) { throw new ConfigurationException(String.format( "datasource '%s' was not found in cluster '%s'", dsName, clusterName)); } return foundDs; } /** * Writes out the configuration for all datasources in the map. * * @param clusterName * @param dataSourceMap * @throws ConfigurationException */ public synchronized void storeDataSourceConfig(String clusterName, Map<String, TungstenProperties> dataSourceMap) throws ConfigurationException { if (logger.isDebugEnabled()) { logger.debug(String.format( "Storing the data source configuration for service='%s'", clusterName)); } for (TungstenProperties ds : dataSourceMap.values()) { if (logger.isDebugEnabled()) { logger.debug(String.format("Storing the data source '%s@%s'", ds.getString("name"), clusterName)); } storeDataSourceConfig(clusterName, ds); } } /** * Writes out a single datasource configuration * * @param clusterName * @param ds * @throws ConfigurationException */ public synchronized void storeDataSourceConfig(String clusterName, TungstenProperties ds) throws ConfigurationException { storeResourceConfig(clusterName, ResourceType.DATASOURCE, ds); } /** * @return the autoEnable */ public boolean isAutoEnable() { return autoEnable; } /** * @param autoEnable the autoEnable to set */ public void setAutoEnable(boolean autoEnable) { this.autoEnable = autoEnable; this.props.setBoolean("autoEnable", autoEnable); } /** * @return the waitForDisconnect */ public boolean isWaitForDisconnect() { return waitForDisconnect; } /** * @param waitForDisconnect the waitForDisconnect to set */ public void setWaitForDisconnect(boolean waitForDisconnect) { this.waitForDisconnect = waitForDisconnect; this.props.setBoolean("waitForDisconnect", waitForDisconnect); } /** * @return the waitForDisconnectTimeout */ public int getWaitForDisconnectTimeout() { return waitForDisconnectTimeout; } /** * @param waitForDisconnectTimeout the waitForDisconnectTimeout to set */ public void setWaitForDisconnectTimeout(int waitForDisconnectTimeout) { this.waitForDisconnectTimeout = waitForDisconnectTimeout; this.props.setInt("waitForDisconnectTimeout", waitForDisconnectTimeout); } /** * @return the dataSourceMap */ public Map<String, Map<String, TungstenProperties>> getDataServicesMap() throws ConfigurationException { return loadClusterDataSourceMap(); } /** * Returns the waitIfUnavailable value. * * @return Returns the waitIfUnavailable. */ public boolean getWaitIfUnavailable() { return waitIfUnavailable; } /** * Sets the waitIfUnavailable value. * * @param waitIfUnavailable The waitIfUnavailable to set. */ public void setWaitIfUnavailable(boolean waitIfUnavailable) { this.waitIfUnavailable = waitIfUnavailable; this.props.setBoolean("waitIfUnavailable", waitIfUnavailable); } /** * Returns the waitIfUnavailableTimeout value. * * @return Returns the waitIfUnavailableTimeout. */ public int getWaitIfUnavailableTimeout() { return waitIfUnavailableTimeout; } public boolean getShowRelativeLatency() { return showRelativeLatency; } /** * Sets the waitIfUnavailableTimeout value. * * @param waitIfUnavailableTimeout The waitIfUnavailableTimeout to set. */ public void setWaitIfUnavailableTimeout(int waitIfUnavailableTimeout) { this.waitIfUnavailableTimeout = waitIfUnavailableTimeout; this.props.setInt("waitIfUnavailableTimeout", waitIfUnavailableTimeout); } // /** // * Returns the routerListenerClass value. // * // * @return Returns the routerListenerClass. // */ // public String getRouterListenerClass() // { // return routerListenerClass; // } // /** // * Sets the routerListenerClass value. // * // * @param routerListenerClass The routerListenerClass to set. // */ // public void setRouterListenerClass(String routerListenerClass) // { // this.routerListenerClass = routerListenerClass; // } public boolean isWaitIfDisabled() { return waitIfDisabled; } public void setWaitIfDisabled(boolean waitIfDisabled) { this.waitIfDisabled = waitIfDisabled; this.props.setBoolean("waitIfDisabled", waitIfDisabled); } public int getWaitIfDisabledTimeout() { return waitIfDisabledTimeout; } public void setWaitIfDisabledTimeout(int waitIfDisabledTimeout) { this.waitIfDisabledTimeout = waitIfDisabledTimeout; this.props.setInt("waitIfDisabledTimeout", waitIfDisabledTimeout); } public void setShowRelativeLatency(boolean showRelativeLatency) { this.showRelativeLatency = showRelativeLatency; this.props.setBoolean("showRelativeLatency", showRelativeLatency); } public String getServiceName() { return serviceName; } public void setServiceName(String serviceName) { this.serviceName = serviceName; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public String getHost() { return host; } public void setHost(String host) { this.host = host; } public int getNotifyPort() { return notifyPort; } public void setNotifyPort(int notifyPort) { this.notifyPort = notifyPort; } public String getNotifierMonitorClass() { return notifierMonitorClass; } public void setNotifierMonitorClass(String notifierMonitorClass) { this.notifierMonitorClass = notifierMonitorClass; } public String getDataSourceLoadBalancer_RO_RELAXED() { return dataSourceLoadBalancer_RO_RELAXED; } public void setDataSourceLoadBalancer_RO_RELAXED( String dataSourceLoadBalancer_RO_RELAXED) { this.dataSourceLoadBalancer_RO_RELAXED = dataSourceLoadBalancer_RO_RELAXED; } public String getClusterMemberName() { return clusterMemberName; } public void setClusterMemberName(String clusterMemberName) { this.clusterMemberName = clusterMemberName; } public boolean isRrIncludeMaster() { return rrIncludeMaster; } public void setRrIncludeMaster(boolean rrIncludeMaster) { this.rrIncludeMaster = rrIncludeMaster; } public void setManagerList(List<String> list) { this.managerList = list; } public List<String> getManagerList() { return managerList; } public int getRouterGatewayPort() { return routerGatewayPort; } public void setRouterGatewayPort(int port) { this.routerGatewayPort = port; } public String getC3p0JmxUrl() { return c3p0JMXUrl; } public void setC3p0JMXUrl(String url) { c3p0JMXUrl = url; } public int getDelayBeforeOnHoldIfNoManager() { return delayBeforeOnHoldIfNoManager; } public void setDelayBeforeOnHoldIfNoManager(int delayBeforeOnHoldIfNoManager) { this.delayBeforeOnHoldIfNoManager = delayBeforeOnHoldIfNoManager; } public int getDelayBeforeOfflineIfNoManager() { return delayBeforeOfflineIfNoManager; } public void setDelayBeforeOfflineIfNoManager( int delayBeforeOfflineIfNoManager) { this.delayBeforeOfflineIfNoManager = delayBeforeOfflineIfNoManager; } public int getDelayBeforeOfflineInMaintenanceModeIfNoManager() { return delayBeforeOfflineInMaintenanceModeIfNoManager; } public void setDelayBeforeOfflineInMaintenanceModeIfNoManager(int delayInS) { delayBeforeOfflineInMaintenanceModeIfNoManager = delayInS; } public int getKeepAliveTimeout() { return keepAliveTimeout; } public void setKeepAliveTimeout(int timeoutInMs) { keepAliveTimeout = timeoutInMs; } public void setGatewayConnectTimeoutMs(int timeoutMs) { gatewayConnectTimeoutMs = timeoutMs; } public int getGatewayConnectTimeoutMs() { return gatewayConnectTimeoutMs; } public void setGatewayLocalBindStartingPort(int port) { if (port < 1024) { port = 1024; } gatewayLocalBindStartingPort = port; } public int getGatewayLocalBindStartingPort() { return gatewayLocalBindStartingPort; } @Override public Object clone() { Object o = null; try { o = super.clone(); } catch (CloneNotSupportedException cnse) { // Should never happen logger.fatal("Unable to clone this RouterConfiguration", cnse); } return o; } /** * Returns the routerClientThreadsPerService value. * * @return Returns the routerClientThreadsPerService. */ public int getRouterClientThreadsPerService() { return routerClientThreadsPerService; } /** * Sets the routerClientThreadsPerService value. * * @param routerClientThreadsPerService The routerClientThreadsPerService to * set. */ public void setRouterClientThreadsPerService( int routerClientThreadsPerService) { this.routerClientThreadsPerService = routerClientThreadsPerService; } /** * Checks critical configuration values and throws and exception if invalid * settings are found. TUC-1738. * * @throws ConfigurationException upon first invalid configuration value */ public void validateConfigurationValues() throws ConfigurationException { if (getKeepAliveTimeout() <= 0 || getKeepAliveTimeout() > ConfigurationConstants.KEEP_ALIVE_TIMEOUT_MAX) { throw new ConfigurationException( "Detected invalid keepAliveTimeout of " + getKeepAliveTimeout() + "ms in router.properties. keepAliveTimeout must be positive and lower than " + ConfigurationConstants.KEEP_ALIVE_TIMEOUT_MAX + " (" + ConfigurationConstants.KEEP_ALIVE_TIMEOUT_MAX / 60000 + "min)."); } // CONT-1485: we no longer put constraints on the delayBeforeOffline if (getGatewayConnectTimeoutMs() <= 0 || getGatewayConnectTimeoutMs() >= ConfigurationConstants.GATEWAY_CONNECT_TIMEOUT_MS_MAX) { throw new ConfigurationException( "Detected invalid gatewayConnectTimeout of " + getGatewayConnectTimeoutMs() + "ms in router.properties. It must be positive and lower than " + ConfigurationConstants.GATEWAY_CONNECT_TIMEOUT_MS_MAX + "ms."); } } public void setReadCommandRetryTimeoutMs(long timeout) { readCommandRetryTimeoutMs = timeout; } public long getReadCommandRetryTimeoutMs() { return readCommandRetryTimeoutMs; } }