/** * 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): Gilles Rayrat * Contributor(s): */ package com.continuent.tungsten.common.config.cluster; import java.io.File; import java.util.HashMap; import java.util.Map; import org.apache.log4j.Logger; import com.continuent.tungsten.common.config.TungstenProperties; import com.continuent.tungsten.common.mysql.MySQLIOs.ExecuteQueryStatus; import com.continuent.tungsten.common.utils.CLLogLevel; import com.continuent.tungsten.common.utils.CLUtils; /** * Loads a default condition mapping configuration and an optional 'overrides' * condition mapping configuration and serves as the main interface to the * properties exported by these configurations. * * @author <a href="mailto:edward.archibald@continuent.com">Edward Archibald</a> * @version 1.0 */ public class DataServerConditionMappingConfiguration extends ClusterConfiguration { private static Logger logger = Logger.getLogger(DataServerConditionMapping.class); private static Map<ExecuteQueryStatus, DataServerConditionMapping> overrides = null; private static Map<ExecuteQueryStatus, DataServerConditionMapping> defaults = null; private static DataServerConditionMappingConfiguration instance = null; private static long defaultStateMappingModified = -1; private static long overriddenStateMappingModified = -1; private DataServerConditionMappingConfiguration() throws ConfigurationException { super(null); loadConditionMappingIfNeeded(); } private static Map<ExecuteQueryStatus, DataServerConditionMapping> getMappings( TungstenProperties mappingProps) { Map<ExecuteQueryStatus, DataServerConditionMapping> mapping = new HashMap<ExecuteQueryStatus, DataServerConditionMapping>(); for (ExecuteQueryStatus status : ExecuteQueryStatus.values()) { TungstenProperties mappingForStatus = mappingProps.subset(status .toString().toLowerCase() + ".", true); if (mappingForStatus != null) { if (mappingForStatus.size() != 3) { logger.warn(String .format("The mapping for status '%s' is malformed: only contains %d elements", status.toString().toLowerCase(), mappingForStatus.size())); continue; } mapping.put(status, new DataServerConditionMapping(status, mappingForStatus)); } } return mapping; } public static DataServerConditionMappingConfiguration getInstance() throws ConfigurationException { if (instance == null) { instance = new DataServerConditionMappingConfiguration(); } else { loadConditionMappingIfNeeded(); } return instance; } public void store() throws ConfigurationException { TungstenProperties propsToStore = new TungstenProperties(); for (ExecuteQueryStatus key : overrides.keySet()) { String value = overrides.get(key).toString().toLowerCase(); propsToStore.setString(key.toString().toLowerCase(), value); } store(ConfigurationConstants.CLUSTER_STATE_MAP_OVERRIDE_PROPS, propsToStore); } /** * Returns the full path of the data services configuration file. */ public String getConfigFileNameInUse() { return System.getProperty(ConfigurationConstants.CLUSTER_HOME) + File.separator + ConfigurationConstants.CLUSTER_STATE_MAP_OVERRIDE_PROPS; } /** * Returns the ResourceState currently mapped to the query status. * * @param queryStatus * @throws ConfigurationException */ public static DataServerConditionMapping getConditionMapping( ExecuteQueryStatus queryStatus) { try { getInstance(); } catch (ConfigurationException c) { logger.error(String.format( "Exception while attempting to get condition mapping: %s", c)); return new DataServerConditionMapping(); } DataServerConditionMapping mapping = overrides.get(queryStatus); if (mapping == null) { mapping = defaults.get(queryStatus); if (mapping == null) { mapping = new DataServerConditionMapping(queryStatus); logger.warn(String .format("No default mapping found for condition '%s'. Returning: %s", queryStatus, mapping)); } } return mapping; } /** * * Prints out current state mapping. */ public static String showStateMapping() { StringBuilder builder = new StringBuilder(); if (defaults == null) { return builder.append("STATEMAP IS EMPTY").toString(); } for (ExecuteQueryStatus key : ExecuteQueryStatus.values()) { DataServerConditionMapping mapping = overrides.get(key); if (mapping != null) { builder.append(String.format("%s : OVERRIDDEN\n", mapping)); } else { mapping = defaults.get(key); if (mapping == null) { mapping = new DataServerConditionMapping(key); logger.warn(String .format("No default mapping found for condition '%s'. Using: %s", key, mapping)); } builder.append(String.format("%s\n", mapping)); } } return builder.toString(); } public static Map<ExecuteQueryStatus, DataServerConditionMapping> getOverrides() { return overrides; } public static Map<ExecuteQueryStatus, DataServerConditionMapping> getDefaults() { return defaults; } /** * Indicates whether a given config file was modified since the last time it * was accessed based on the time modified that is passed in. * * @param configFileName * @param timeLastModified */ private static long configFileLastModified(String configFileName, long timeLastModified) { File configFile = new File(getGlobalConfigDirName(clusterHomeName), configFileName); if (!configFile.exists()) { return timeLastModified; } if (configFile.lastModified() > timeLastModified) { return configFile.lastModified(); } return timeLastModified; } /** * Loads the condition mapping, initially, and any time one of the condition * mapping files is modified. * * @throws ConfigurationException */ private static synchronized void loadConditionMappingIfNeeded() throws ConfigurationException { boolean loadNeeded = false; long lastModified = -1; if ((lastModified = configFileLastModified( ConfigurationConstants.CLUSTER_STATE_MAP_DEFAULT_PROPS, defaultStateMappingModified)) > defaultStateMappingModified) { CLUtils.println(String.format("%s CONDITION MAPPING FROM %s/%s", (defaultStateMappingModified == -1 ? "LOADING" : "RELOADING"), getGlobalConfigDirName(clusterHomeName), ConfigurationConstants.CLUSTER_STATE_MAP_DEFAULT_PROPS), CLLogLevel.detailed); defaultStateMappingModified = lastModified; loadNeeded = true; } if ((lastModified = configFileLastModified( ConfigurationConstants.CLUSTER_STATE_MAP_OVERRIDE_PROPS, overriddenStateMappingModified)) > overriddenStateMappingModified) { CLUtils.println(String.format("%s CONDITION MAPPING FROM %s/%s", (overriddenStateMappingModified == -1 ? "LOADING" : "RELOADING"), getGlobalConfigDirName(clusterHomeName), ConfigurationConstants.CLUSTER_STATE_MAP_OVERRIDE_PROPS), CLLogLevel.detailed); overriddenStateMappingModified = lastModified; loadNeeded = true; } if (!loadNeeded) { return; } TungstenProperties mapping = getConfiguration(ConfigurationConstants.CLUSTER_STATE_MAP_DEFAULT_PROPS); defaults = getMappings(mapping); try { mapping = getConfiguration(ConfigurationConstants.CLUSTER_STATE_MAP_OVERRIDE_PROPS); overrides = getMappings(mapping); } catch (ConfigurationException c) { overrides = new HashMap<ExecuteQueryStatus, DataServerConditionMapping>(); } CLUtils.println(showStateMapping(), CLLogLevel.detailed); } }