/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.hadoop.corona;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;
/**
* Reloads corona scheduling parameters periodically. Generates the pools
* config if the {@link PoolsConfigDocumentGenerator} class is set.
* Needs to be thread safe
*
* The following is a corona.xml example:
*
* <?xml version="1.0"?>
* <configuration>
* <defaultSchedulingMode>FAIR</defaultSchedulingMode>
* <nodeLocalityWaitMAP>0</nodeLocalityWaitMAP>
* <rackLocalityWaitMAP>5000</rackLocalityWaitMAP>
* <preemptedTaskMaxRunningTime>60000</preemptedTaskMaxRunningTime>
* <shareStarvingRatio>0.9</shareStarvingRatio>
* <starvingTimeForShare>60000</starvingTimeForShare>
* <starvingTimeForMinimum>30000</starvingTimeForMinimum>
* <group name="group_a">
* <minMAP>200</minMAP>
* <minMAP>100</minMAP>
* <minREDUCE>100</minREDUCE>
* <maxMAP>200</maxMAP>
* <maxREDUCE>200</maxREDUCE>
* <pool name="pool_sla">
* <minMAP>100</minMAP>
* <minREDUCE>100</minREDUCE>
* <maxMAP>200</maxMAP>
* <maxREDUCE>200</maxREDUCE>
* <weight>2.0</weight>
* <schedulingMode>FIFO</schedulingMode>
* </pool>
* <pool name="pool_nonsla">
* </pool>
* </group>
* <group name ="group_b">
* <maxMAP>200</maxMAP>
* <maxREDUCE>200</maxREDUCE>
* <weight>3.0</weight>
* </group>
* </configuration>
*
* Note that the type strings "MAP" and "REDUCE" must be
* defined in {@link CoronaConf}
*/
public class ConfigManager {
/** Configuration xml tag name */
public static final String CONFIGURATION_TAG_NAME = "configuration";
/** Redirect xml tag name */
public static final String REDIRECT_TAG_NAME = "redirect";
/** Group xml tag name */
public static final String GROUP_TAG_NAME = "group";
/** Pool xml tag name */
public static final String POOL_TAG_NAME = "pool";
/** Scheduling mode xml tag name */
public static final String SCHEDULING_MODE_TAG_NAME = "schedulingMode";
/** Preemptability xml tag name */
public static final String PREEMPTABILITY_MODE_TAG_NAME = "preemptable";
/** Request maximum xml tag name */
public static final String REQUEST_MAX_MODE_TAG_NAME = "requestMax";
/** Weight xml tag name */
public static final String WEIGHT_TAG_NAME = "weight";
/** Priority xml tag name */
public static final String PRIORITY_TAG_NAME = "priority";
/** Whitelist xml tag name */
public static final String WHITELIST_TAG_NAME = "whitelist";
/** Min xml tag name prefix */
public static final String MIN_TAG_NAME_PREFIX = "min";
/** Max xml tag name prefix */
public static final String MAX_TAG_NAME_PREFIX = "max";
public static final String REDIRECT_JOB_WITH_LIMIT = "redirectJobWithLimit";
/** Name xml attribute */
public static final String NAME_ATTRIBUTE = "name";
/** Source xml attribute (for redirect) */
public static final String SOURCE_ATTRIBUTE = "source";
/** Destination xml attribute (for redirect) */
public static final String DESTINATION_ATTRIBUTE = "destination";
public static final String JOB_INPUT_SIZE_LIMIT_ATTRIBUTE = "inputSizeLimit";
/** Logger */
private static final Log LOG = LogFactory.getLog(ConfigManager.class);
/** The default behavior to schedule from nodes to sessions. */
private static final boolean DEFAULT_SCHEDULE_FROM_NODE_TO_SESSION = false;
/** The default max running time to consider for preemption */
private static final long DEFAULT_PREEMPT_TASK_MAX_RUNNING_TIME =
5 * 60 * 1000L;
/** The default number of attempts to preempt */
private static final int DEFAULT_PREEMPTION_ROUNDS = 10;
/** The default ratio to consider starvation */
private static final double DEFAULT_SHARE_STARVING_RATIO = 0.7;
/** The minimum amount of time to wait between preemptions */
public static final long DEFAULT_MIN_PREEMPT_PERIOD = 60 * 1000L;
/**
* The default number of grants to give out on each iteration of
* the Scheduler.
*/
public static final int DEFAULT_GRANTS_PER_ITERATION = 5000;
/** The default allowed starvation time for a share */
public static final long DEFAULT_STARVING_TIME_FOR_SHARE = 5 * 60 * 1000L;
/** The default allowed starvation time for a minimum allocation */
public static final long DEFAULT_STARVING_TIME_FOR_MINIMUM = 3 * 60 * 1000L;
/** The comparator to use by default within any pool group */
private static final ScheduleComparator DEFAULT_POOL_GROUP_COMPARATOR =
ScheduleComparator.PRIORITY;
/** The comparator to use by default within any pool */
private static final ScheduleComparator DEFAULT_POOL_COMPARATOR =
ScheduleComparator.FIFO;
/** If defined, generate the pools config document periodically */
private PoolsConfigDocumentGenerator poolsConfigDocumentGenerator;
/** The types this configuration is initialized for */
private final Collection<ResourceType> TYPES;
/** The classloader to use when looking for resource in the classpath */
private final ClassLoader classLoader;
/** Set of configured pool group names */
private Set<String> poolGroupNameSet;
/** Set of configured pool info names */
private Set<PoolInfo> poolInfoSet;
/** The Map of max allocations for a given type and group */
private TypePoolGroupNameMap<Integer> typePoolGroupNameToMax =
new TypePoolGroupNameMap<Integer>();
/** The Map of min allocations for a given type and group */
private TypePoolGroupNameMap<Integer> typePoolGroupNameToMin =
new TypePoolGroupNameMap<Integer>();
/** The Map of max allocations for a given type, group, and pool */
private TypePoolInfoMap<Integer> typePoolInfoToMax =
new TypePoolInfoMap<Integer>();
/** The Map of min allocations for a given type, group, and pool */
private TypePoolInfoMap<Integer> typePoolInfoToMin =
new TypePoolInfoMap<Integer>();
/** The set of pools that can't be preempted */
private Set<PoolInfo> nonPreemptablePools = new HashSet<PoolInfo>();
/**
* The set of pools that limit the number of resource requests by the
* pool maximum.
*/
private Set<PoolInfo> requestMaxPools = new HashSet<PoolInfo>();
/** The Map of node locality wait times for different resource types */
private Map<ResourceType, Long> typeToNodeWait;
/** The Map of rack locality wait times for different resource types */
private Map<ResourceType, Long> typeToRackWait;
/** The Map of comparators configurations for the pools */
private Map<PoolInfo, ScheduleComparator> poolInfoToComparator;
/** The Map of the weights configuration for the pools */
private Map<PoolInfo, Double> poolInfoToWeight;
/** The Map of redirections (source -> target) for PoolInfo objects */
private Map<PoolInfo, PoolInfo> poolInfoToRedirect;
/** The Map of priorities for the pools */
private Map<PoolInfo, Integer> poolInfoToPriority;
/** The Map of whitelist for the pools */
private Map<PoolInfo, String> poolInfoToWhitelist;
/** The default comparator for the schedulables within the pool */
private ScheduleComparator defaultPoolComparator;
/** The ratio of the share to consider starvation */
private double shareStarvingRatio;
/** The allowed starvation time for the share */
private long starvingTimeForShare;
/** The minimum period between preemptions. */
private long minPreemptPeriod;
/** The maximum Job size for a pool */
private Map<PoolInfo, Long> poolJobSizeLimit;
/** The Map of redirections when job exceeds limit for Pool */
private Map<PoolInfo, PoolInfo> jobExceedsLimitPoolRedirect;
/** The default FIFO pool info for large query to redirect to */
private PoolInfo defaultFifoPoolInfo;
/** Tasks to schedule in one iteration of the scheduler */
private volatile int grantsPerIteration = DEFAULT_GRANTS_PER_ITERATION;
/** The allowed starvation time for the min allocation */
private long starvingTimeForMinimum;
/** The max time of the task to consider for preemption */
private long preemptedTaskMaxRunningTime;
/** The number of times to iterate over pools trying to preempt */
private int preemptionRounds;
/** Match nodes to session */
private boolean scheduleFromNodeToSession;
/** The flag for the reload thread */
private volatile boolean running = true;
/** The thread that monitors and reloads the config file */
private ReloadThread reloadThread;
/** The last timestamp when the config was successfully loaded */
private long lastSuccessfulReload = -1L;
/** Corona conf used to get static config */
private final CoronaConf conf;
/** Pools reload period ms */
private final long poolsReloadPeriodMs;
/** Config reload period ms */
private final long configReloadPeriodMs;
/** The name of the general config file to use */
private volatile String configFileName;
/** The name of the pools config file to use */
private volatile String poolsConfigFileName;
/**
* The main constructor for the config manager given the types and the name
* of the config file to use
* @param types the types to initialize the configuration for
*/
public ConfigManager(
Collection<ResourceType> types, CoronaConf conf) {
this.TYPES = types;
this.conf = conf;
Class<?> poolsConfigDocumentGeneratorClass =
conf.getPoolsConfigDocumentGeneratorClass();
if (poolsConfigDocumentGeneratorClass != null) {
try {
this.poolsConfigDocumentGenerator = (PoolsConfigDocumentGenerator)
poolsConfigDocumentGeneratorClass.newInstance();
poolsConfigDocumentGenerator.initialize(conf);
} catch (InstantiationException e) {
LOG.warn("Failed to instantiate " +
poolsConfigDocumentGeneratorClass, e);
} catch (IllegalAccessException e) {
LOG.warn("Failed to instantiate " +
poolsConfigDocumentGeneratorClass, e);
}
} else {
poolsConfigDocumentGenerator = null;
}
poolsReloadPeriodMs = conf.getPoolsReloadPeriodMs();
configReloadPeriodMs = conf.getConfigReloadPeriodMs();
LOG.info("ConfigManager: PoolsConfigDocumentGenerator class = " +
poolsConfigDocumentGeneratorClass + ", poolsReloadPeriodMs = " +
poolsReloadPeriodMs + ", configReloadPeriodMs = " +
configReloadPeriodMs);
typeToNodeWait = new EnumMap<ResourceType, Long>(ResourceType.class);
typeToRackWait = new EnumMap<ResourceType, Long>(ResourceType.class);
defaultPoolComparator = DEFAULT_POOL_COMPARATOR;
shareStarvingRatio = DEFAULT_SHARE_STARVING_RATIO;
minPreemptPeriod = DEFAULT_MIN_PREEMPT_PERIOD;
grantsPerIteration = DEFAULT_GRANTS_PER_ITERATION;
starvingTimeForMinimum = DEFAULT_STARVING_TIME_FOR_MINIMUM;
starvingTimeForShare = DEFAULT_STARVING_TIME_FOR_SHARE;
preemptedTaskMaxRunningTime = DEFAULT_PREEMPT_TASK_MAX_RUNNING_TIME;
preemptionRounds = DEFAULT_PREEMPTION_ROUNDS;
scheduleFromNodeToSession = DEFAULT_SCHEDULE_FROM_NODE_TO_SESSION;
reloadThread = new ReloadThread();
reloadThread.setName("Config reload thread");
reloadThread.setDaemon(true);
for (ResourceType type : TYPES) {
typeToNodeWait.put(type, 0L);
typeToRackWait.put(type, 0L);
}
ClassLoader cl = Thread.currentThread().getContextClassLoader();
if (cl == null) {
cl = ConfigManager.class.getClassLoader();
}
classLoader = cl;
if (poolsConfigDocumentGenerator != null) {
if (generatePoolsConfigIfClassSet() == null) {
throw new IllegalStateException("Failed to generate the pools " +
"config. Must succeed on initialization of ConfigManager.");
}
}
try {
findConfigFiles();
reloadAllConfig(true);
} catch (IOException e) {
LOG.error("Failed to load " + configFileName, e);
} catch (SAXException e) {
LOG.error("Failed to load " + configFileName, e);
} catch (ParserConfigurationException e) {
LOG.error("Failed to load " + configFileName, e);
} catch (JSONException e) {
LOG.error("Failed to load " + configFileName, e);
}
}
/**
* Used for unit tests
*/
public ConfigManager() {
TYPES = null;
conf = new CoronaConf(new Configuration());
poolsReloadPeriodMs = conf.getPoolsReloadPeriodMs();
configReloadPeriodMs = conf.getConfigReloadPeriodMs();
classLoader = ConfigManager.class.getClassLoader();
}
/**
* Find the configuration files as set file names or in the classpath.
*/
private void findConfigFiles() {
// Find the materialized_JSON configuration file.
if (configFileName == null) {
String jsonConfigFileString = conf.getConfigFile().replace(
CoronaConf.DEFAULT_CONFIG_FILE,
Configuration.MATERIALIZEDJSON);
File jsonConfigFile = new File(jsonConfigFileString);
String jsonConfigFileName = null;
if (jsonConfigFile.exists()) {
jsonConfigFileName = jsonConfigFileString;
} else {
URL u = classLoader.getResource(jsonConfigFileString);
jsonConfigFileName = (u != null) ? u.getPath() : null;
}
// Check that the materialized_JSON contains the resources
// of corona.xml
if (jsonConfigFileName != null) {
try {
jsonConfigFile = new File(jsonConfigFileName);
InputStream in = new BufferedInputStream(new FileInputStream(
jsonConfigFile));
JSONObject json = conf.instantiateJsonObject(in);
if (json.has(conf.xmlToThrift(CoronaConf.DEFAULT_CONFIG_FILE))) {
configFileName = jsonConfigFileName;
LOG.info("Attempt to find config file " + jsonConfigFileString +
" as a file and in class loader returned " + configFileName);
}
} catch (IOException e) {
LOG.warn("IOException: " + "while parsing corona JSON configuration");
} catch (JSONException e) {
LOG.warn("JSONException: " + "while parsing corona JSON configuration");
}
}
}
if (configFileName == null) {
// Parsing the JSON configuration failed. Look for
// the xml configuration.
String configFileString = conf.getConfigFile();
File configFile = new File(configFileString);
if (configFile.exists()) {
configFileName = configFileString;
} else {
URL u = classLoader.getResource(configFileString);
configFileName = (u != null) ? u.getPath() : null;
}
LOG.info("Attempt to find config file " + configFileString +
" as a file and in class loader returned " + configFileName);
}
if (poolsConfigFileName == null) {
String poolsConfigFileString = conf.getPoolsConfigFile();
File poolsConfigFile = new File(poolsConfigFileString);
if (poolsConfigFile.exists()) {
poolsConfigFileName = poolsConfigFileString;
} else {
URL u = classLoader.getResource(poolsConfigFileString);
poolsConfigFileName = (u != null) ? u.getPath() : null;
}
LOG.info("Attempt to find pools config file " + poolsConfigFileString +
" as a file and in class loader returned " + poolsConfigFileName);
}
}
/**
* Start monitoring the configuration for the updates
*/
public void start() {
reloadThread.start();
}
/**
* Stop monitoring and reloading of the configuration
*/
public void close() {
running = false;
reloadThread.interrupt();
}
/**
* Is this a configured pool group?
* @param poolGroup Name of the pool group to check
* @return True if configured, false otherwise
*/
public synchronized boolean isConfiguredPoolGroup(String poolGroup) {
if (poolGroupNameSet == null) {
return false;
}
return poolGroupNameSet.contains(poolGroup);
}
/**
* Is this a configured pool info?
* @param poolInfo Pool info to check
* @return True if configured, false otherwise
*/
public synchronized boolean isConfiguredPoolInfo(PoolInfo poolInfo) {
if (poolInfoSet == null) {
return false;
}
return poolInfoSet.contains(poolInfo);
}
/**
* Get the configured pool infos so that the PoolGroupManager can make sure
* they are created for stats and cm.jsp.
*/
public synchronized Collection<PoolInfo> getConfiguredPoolInfos() {
return poolInfoSet;
}
/**
* Get the configured maximum allocation for a given {@link ResourceType}
* in a given pool group
* @param poolGroupName the name of the pool group
* @param type the type of the resource
* @return the maximum allocation for the resource in a pool group
*/
public synchronized int getPoolGroupMaximum(String poolGroupName,
ResourceType type) {
Integer max = (typePoolGroupNameToMax == null) ? null :
typePoolGroupNameToMax.get(type, poolGroupName);
return max == null ? Integer.MAX_VALUE : max;
}
/**
* Get the configured minimum allocation for a given {@link ResourceType}
* in a given pool group
* @param poolGroupName the name of the pool group
* @param type the type of the resource
* @return the minimum allocation for the resource in a pool group
*/
public synchronized int getPoolGroupMinimum(String poolGroupName,
ResourceType type) {
Integer min = (typePoolGroupNameToMin == null) ? null :
typePoolGroupNameToMin.get(type, poolGroupName);
return min == null ? 0 : min;
}
/**
* Get the configured ScheduleComparator.
* in a given pool group
* @param poolGroupName the name of the pool group
* @return the configured comparator. (returns DEFAULT_POOL_GROUP_COMPARATOR
* by default.
*/
public ScheduleComparator getPoolGroupComparator(String poolGroupName) {
// TODO: If it uses a shared data structure, this needs to be thread safe!!
// TODO: Currently unconfigurable.
return DEFAULT_POOL_GROUP_COMPARATOR;
}
/**
* Get the configured maximum allocation for a given {@link ResourceType}
* in a given pool
* @param poolInfo Pool info to check
* @param type the type of the resource
* @return the maximum allocation for the resource in a pool
*/
public synchronized int getPoolMaximum(PoolInfo poolInfo, ResourceType type) {
Integer max = (typePoolInfoToMax == null) ? null :
typePoolInfoToMax.get(type, poolInfo);
return max == null ? Integer.MAX_VALUE : max;
}
/**
* Get the configured minimum allocation for a given {@link ResourceType}
* in a given pool
* @param poolInfo Pool info to check
* @param type the type of the resource
* @return the minimum allocation for the resource in a pool
*/
public synchronized int getPoolMinimum(PoolInfo poolInfo, ResourceType type) {
Integer min = (typePoolInfoToMin == null) ? null :
typePoolInfoToMin.get(type, poolInfo);
return min == null ? 0 : min;
}
public synchronized boolean isPoolPreemptable(PoolInfo poolInfo) {
return !nonPreemptablePools.contains(poolInfo);
}
/**
* Should this pool use the request max to limit job submission?
* @param poolInfo Pool info to check
* @return True if using the request max to limit jobs
*/
public synchronized boolean useRequestMax(PoolInfo poolInfo) {
return requestMaxPools.contains(poolInfo);
}
/**
* Get the weight for the pool
* @param poolInfo Pool info to check
* @return the weight for the pool
*/
public synchronized double getWeight(PoolInfo poolInfo) {
Double weight = (poolInfoToWeight == null) ? null :
poolInfoToWeight.get(poolInfo);
return weight == null ? 1.0 : weight;
}
/**
* Get the priority for the pool
* @param poolInfo Pool info to check
* @return the priority for the pool
*/
public synchronized int getPriority(PoolInfo poolInfo) {
Integer priority = (poolInfoToPriority == null) ? null :
poolInfoToPriority.get(poolInfo);
return priority == null ? 0 : priority;
}
/**
* Get a redirected PoolInfo (destination) from the source. Only supports
* one level of redirection.
* @param poolInfo Pool info to check for a destination
* @return Destination pool info if one exists, else return the input
*/
public synchronized PoolInfo getRedirect(PoolInfo poolInfo) {
PoolInfo destination = (poolInfoToRedirect == null) ? poolInfo :
poolInfoToRedirect.get(poolInfo);
if (destination == null) {
return poolInfo;
}
return destination;
}
private synchronized boolean jobExceedsSizeInfoLimit(Long jobSizeInfo,
Long sizeInfoLimit) {
return jobSizeInfo >= sizeInfoLimit;
}
public synchronized PoolInfo getRedirect(PoolInfo poolInfo, Long jobSizeInfo) {
// Check if the pool specified has a redirect, use result to determine
// if we should redirect based on size info limit
PoolInfo actualPoolInfo = getRedirect(poolInfo);
Long sizeInfoLimit = poolJobSizeLimit.get(actualPoolInfo);
if(sizeInfoLimit != null && jobExceedsSizeInfoLimit(jobSizeInfo, sizeInfoLimit)) {
return jobExceedsLimitPoolRedirect.get(actualPoolInfo);
}
return actualPoolInfo;
}
public synchronized String getWhitelist(PoolInfo poolInfo) {
return poolInfoToWhitelist.get(poolInfo);
}
/**
* Get a copy of the map of redirects (used for cm.jsp)
*
* @return Map of redirects otherwise null if none exists
*/
public synchronized Map<PoolInfo, PoolInfo> getRedirects() {
return (poolInfoToRedirect == null) ? null :
new HashMap<PoolInfo, PoolInfo>(poolInfoToRedirect);
}
/**
* Get the comparator to use for scheduling sessions within a pool
* @param poolInfo Pool info to check
* @return the scheduling comparator to use for the pool
*/
public synchronized ScheduleComparator getPoolComparator(PoolInfo poolInfo) {
ScheduleComparator comparator =
(poolInfoToComparator == null) ? null :
poolInfoToComparator.get(poolInfo);
return comparator == null ? defaultPoolComparator : comparator;
}
public synchronized long getPreemptedTaskMaxRunningTime() {
return preemptedTaskMaxRunningTime;
}
public synchronized boolean getScheduleFromNodeToSession() {
return scheduleFromNodeToSession;
}
public synchronized int getPreemptionRounds() {
return preemptionRounds;
}
public synchronized double getShareStarvingRatio() {
return shareStarvingRatio;
}
public synchronized long getStarvingTimeForShare() {
return starvingTimeForShare;
}
public synchronized long getStarvingTimeForMinimum() {
return starvingTimeForMinimum;
}
/**
* Get the locality wait to be used by the scheduler for a given
* ResourceType on a given LocalityLevel
* @param type the type of the resource
* @param level the locality level
* @return the time in milliseconds to use as a locality wait for a resource
* of a given type on a given level
*/
public synchronized long getLocalityWait(ResourceType type,
LocalityLevel level) {
if (level == LocalityLevel.ANY) {
return 0L;
}
Long wait = level == LocalityLevel.NODE ?
typeToNodeWait.get(type) : typeToRackWait.get(type);
if (wait == null) {
throw new IllegalArgumentException("Unknown type:" + type);
}
return wait;
}
public long getMinPreemptPeriod() {
return minPreemptPeriod;
}
public int getGrantsPerIteration() {
return grantsPerIteration;
}
/**
* The thread that monitors the config file and reloads the configuration
* when the file is updated
*/
private class ReloadThread extends Thread {
@Override
public void run() {
long lastReloadAttempt = -1L;
long lastGenerationAttempt = -1L;
while (running) {
try {
boolean reloadAllConfig = false;
long now = ClusterManager.clock.getTime();
if ((poolsConfigDocumentGenerator != null) &&
(now - lastGenerationAttempt > poolsReloadPeriodMs)) {
lastGenerationAttempt = now;
generatePoolsConfigIfClassSet();
reloadAllConfig = true;
}
if (now - lastReloadAttempt > configReloadPeriodMs) {
lastReloadAttempt = now;
reloadAllConfig = true;
}
if (reloadAllConfig) {
findConfigFiles();
try {
reloadAllConfig(false);
} catch (IOException e) {
LOG.error("Failed to load " + configFileName, e);
} catch (SAXException e) {
LOG.error("Failed to load " + configFileName, e);
} catch (ParserConfigurationException e) {
LOG.error("Failed to load " + configFileName, e);
} catch (JSONException e) {
LOG.error("Failed to load " + configFileName, e);
}
}
} catch (Exception e) {
LOG.error("Failed to reload config because of " +
"an unknown exception", e);
}
try {
Thread.sleep(
Math.min(poolsReloadPeriodMs, configReloadPeriodMs) / 10);
} catch (InterruptedException e) {
LOG.warn("run: Interrupted", e);
}
}
}
}
/**
* Generate the new pools configuration using the configuration generator.
* The generated configuration is written to a temporary file and then
* atomically renamed to the specified destination file.
* This function may be called concurrently and it is safe to do so because
* of the atomic rename to the destination file.
*
* @return Md5 of the generated file or null if generation failed.
*/
public String generatePoolsConfigIfClassSet() {
if (poolsConfigDocumentGenerator == null) {
return null;
}
Document document = poolsConfigDocumentGenerator.generatePoolsDocument();
if (document == null) {
LOG.warn("generatePoolsConfig: Did not generate a valid pools xml file");
return null;
}
// Write the content into a temporary xml file and rename to the
// expected file.
File tempXmlFile;
try {
TransformerFactory transformerFactory = TransformerFactory.newInstance();
transformerFactory.setAttribute("indent-number", new Integer(2));
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(
"{http://xml.apache.org/xslt}indent-amount", "2");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
DOMSource source = new DOMSource(document);
tempXmlFile = File.createTempFile("tmpPoolsConfig", "xml");
if (LOG.isDebugEnabled()) {
StreamResult stdoutResult = new StreamResult(System.out);
transformer.transform(source, stdoutResult);
}
StreamResult result = new StreamResult(tempXmlFile);
transformer.transform(source, result);
String md5 = org.apache.commons.codec.digest.DigestUtils.md5Hex(
new FileInputStream(tempXmlFile));
File destXmlFile = new File(conf.getPoolsConfigFile());
boolean success = tempXmlFile.renameTo(destXmlFile);
LOG.info("generatePoolConfig: Renamed generated file " +
tempXmlFile.getAbsolutePath() + " to " +
destXmlFile.getAbsolutePath() + " returned " + success +
" with md5sum " + md5);
return md5;
} catch (TransformerConfigurationException e) {
LOG.warn("generatePoolConfig: Failed to write file", e);
} catch (IOException e) {
LOG.warn("generatePoolConfig: Failed to write file", e);
} catch (TransformerException e) {
LOG.warn("generatePoolConfig: Failed to write file", e);
}
return null;
}
/**
* Helper function to reloadJsonConfig(). Parses the JSON Object
* corresponding to the key "TYPES".
* @throws JSONException
*/
private void loadLocalityWaits(JSONObject json, Map<ResourceType, Long>
newTypeToNodeWait, Map<ResourceType, Long> newTypeToRackWait)
throws JSONException {
Iterator<String> keys = json.keys();
while (keys.hasNext()) {
String key = keys.next();
if (!json.isNull(key)) {
for (ResourceType type : TYPES) {
if (key.equals("nodeLocalityWait" + type)) {
long val = Long.parseLong(json.getString(key));
newTypeToNodeWait.put(type, val);
}
if (key.equals("rackLocalityWait" + type)) {
long val = Long.parseLong(json.getString(key));
newTypeToRackWait.put(type, val);
}
}
}
}
}
private boolean loadPoolInfoToRedirect(String source, String destination,
Map<PoolInfo, PoolInfo> newPoolInfoToRedirect) {
PoolInfo sourcePoolInfo = PoolInfo.createPoolInfo(source);
PoolInfo destPoolInfo = PoolInfo.createPoolInfo(destination);
if (sourcePoolInfo == null || destPoolInfo == null) {
LOG.error("Illegal redirect source " + sourcePoolInfo + " or destination " +
destPoolInfo);
return false;
} else {
newPoolInfoToRedirect.put(sourcePoolInfo, destPoolInfo);
return true;
}
}
/**
* Helper function to reloadJsonConfig(). Parses the JSON Array
* corresponding to the key REDIRECT_TAG_NAME.
* @throws JSONException
*/
private void loadPoolInfoToRedirect(JSONArray json, Map<PoolInfo, PoolInfo>
newPoolInfoToRedirect) throws JSONException {
for (int i=0; i < json.length(); i++) {
JSONObject jsonObj = json.getJSONObject(i);
loadPoolInfoToRedirect(jsonObj.getString(SOURCE_ATTRIBUTE),
jsonObj.getString(DESTINATION_ATTRIBUTE),
newPoolInfoToRedirect);
}
}
private void loadPoolInfoToRedirect(String poolGroupName, String source, String destination,
String specifiedJobInputSizeLimit,
Map<PoolInfo, PoolInfo> newPoolInfoToRedirectWithLimit,
Map<PoolInfo, Long> newPoolInfoJobSizeLimit) {
String validSource = PoolInfo.createValidString(poolGroupName, source);
String validDestination = PoolInfo.createValidString(poolGroupName, destination);
boolean loadedRedirect = loadPoolInfoToRedirect(validSource, validDestination, newPoolInfoToRedirectWithLimit);
if (!loadedRedirect) { return; }
long jobInputSizeLimit = Long.parseLong(specifiedJobInputSizeLimit);
if (jobInputSizeLimit <= 0) {
LOG.error("Illegal redirect limit " + jobInputSizeLimit + ". Limit must be > 0.");
} else {
PoolInfo sourcePoolInfo = PoolInfo.createPoolInfo(validSource);
newPoolInfoJobSizeLimit.put(sourcePoolInfo, jobInputSizeLimit);
}
}
/**
* Reload the general configuration and update all in-memory values. Should
* be invoked under synchronization.
*
* @throws IOException
* @throws SAXException
* @throws ParserConfigurationException
* @throws JSONException
*/
private void reloadJsonConfig() throws
IOException, SAXException, ParserConfigurationException, JSONException {
Map<ResourceType, Long> newTypeToNodeWait;
Map<ResourceType, Long> newTypeToRackWait;
ScheduleComparator newDefaultPoolComparator = DEFAULT_POOL_COMPARATOR;
double newShareStarvingRatio = DEFAULT_SHARE_STARVING_RATIO;
long newMinPreemptPeriod = DEFAULT_MIN_PREEMPT_PERIOD;
int newGrantsPerIteration = DEFAULT_GRANTS_PER_ITERATION;
long newStarvingTimeForMinimum = DEFAULT_STARVING_TIME_FOR_MINIMUM;
long newStarvingTimeForShare = DEFAULT_STARVING_TIME_FOR_SHARE;
long newPreemptedTaskMaxRunningTime = DEFAULT_PREEMPT_TASK_MAX_RUNNING_TIME;
int newPreemptionRounds = DEFAULT_PREEMPTION_ROUNDS;
boolean newScheduleFromNodeToSession = DEFAULT_SCHEDULE_FROM_NODE_TO_SESSION;
newTypeToNodeWait = new EnumMap<ResourceType, Long>(ResourceType.class);
newTypeToRackWait = new EnumMap<ResourceType, Long>(ResourceType.class);
Map<PoolInfo, PoolInfo> newPoolInfoToRedirect =
new HashMap<PoolInfo, PoolInfo>();
for (ResourceType type : TYPES) {
newTypeToNodeWait.put(type, 0L);
newTypeToRackWait.put(type, 0L);
}
// All the configuration files for a cluster are placed in one large
// json object. This large json object has keys that map to smaller
// json objects which hold the same resources as xml configuration
// files. Here, we try to parse the json object that corresponds to
// corona.xml
File jsonConfigFile = new File(configFileName);
InputStream in = new BufferedInputStream(new FileInputStream(jsonConfigFile));
JSONObject json = conf.instantiateJsonObject(in);
json = json.getJSONObject(conf.xmlToThrift(CoronaConf.DEFAULT_CONFIG_FILE));
Iterator<String> keys = json.keys();
while (keys.hasNext()) {
String key = keys.next();
if (!json.isNull(key)) {
if (key.equals("localityWaits")) {
JSONObject jsonTypes = json.getJSONObject(key);
loadLocalityWaits(jsonTypes, newTypeToNodeWait, newTypeToRackWait);
}
if (key.equals("defaultSchedulingMode")) {
newDefaultPoolComparator =
ScheduleComparator.valueOf(json.getString(key));
}
if (key.equals("shareStarvingRatio")) {
newShareStarvingRatio = json.getDouble(key);
if (newShareStarvingRatio < 0 || newShareStarvingRatio > 1.0) {
LOG.error("Illegal shareStarvingRatio:" + newShareStarvingRatio);
newShareStarvingRatio = DEFAULT_SHARE_STARVING_RATIO;
}
}
if (key.equals("grantsPerIteration")) {
newGrantsPerIteration = json.getInt(key);
if (newMinPreemptPeriod < 0) {
LOG.error("Illegal grantsPerIteration: " + newGrantsPerIteration);
newGrantsPerIteration = DEFAULT_GRANTS_PER_ITERATION;
}
}
if (key.equals("minPreemptPeriod")) {
newMinPreemptPeriod = json.getLong(key);
if (newMinPreemptPeriod < 0) {
LOG.error("Illegal minPreemptPeriod: " + newMinPreemptPeriod);
newMinPreemptPeriod = DEFAULT_MIN_PREEMPT_PERIOD;
}
}
if (key.equals("starvingTimeForShare")) {
newStarvingTimeForShare = json.getLong(key);
if (newStarvingTimeForShare < 0) {
LOG.error("Illegal starvingTimeForShare:" + newStarvingTimeForShare);
newStarvingTimeForShare = DEFAULT_STARVING_TIME_FOR_SHARE;
}
}
if (key.equals("starvingTimeForMinimum")) {
newStarvingTimeForMinimum = json.getLong(key);
if (newStarvingTimeForMinimum < 0) {
LOG.error("Illegal starvingTimeForMinimum:" +
newStarvingTimeForMinimum);
newStarvingTimeForMinimum = DEFAULT_STARVING_TIME_FOR_MINIMUM;
}
}
if (key.equals("preemptedTaskMaxRunningTime")) {
newPreemptedTaskMaxRunningTime = json.getLong(key);
if (newPreemptedTaskMaxRunningTime < 0) {
LOG.error("Illegal preemptedTaskMaxRunningTime:" +
newPreemptedTaskMaxRunningTime);
newPreemptedTaskMaxRunningTime =
DEFAULT_PREEMPT_TASK_MAX_RUNNING_TIME;
}
}
if (key.equals("preemptionRounds")) {
newPreemptionRounds = json.getInt(key);
if (newPreemptionRounds < 0) {
LOG.error("Illegal preemptedTaskMaxRunningTime:" +
newPreemptionRounds);
newPreemptionRounds = DEFAULT_PREEMPTION_ROUNDS;
}
}
if (key.equals("scheduleFromNodeToSession")) {
newScheduleFromNodeToSession = json.getBoolean(key);
}
if (key.equals(REDIRECT_TAG_NAME)) {
JSONArray jsonPoolInfoToRedirect = json.getJSONArray(key);
loadPoolInfoToRedirect(jsonPoolInfoToRedirect,
newPoolInfoToRedirect);
}
}
}
synchronized (this) {
this.typeToNodeWait = newTypeToNodeWait;
this.typeToRackWait = newTypeToRackWait;
this.defaultPoolComparator = newDefaultPoolComparator;
this.shareStarvingRatio = newShareStarvingRatio;
this.minPreemptPeriod = newMinPreemptPeriod;
this.grantsPerIteration = newGrantsPerIteration;
this.starvingTimeForMinimum = newStarvingTimeForMinimum;
this.starvingTimeForShare = newStarvingTimeForShare;
this.preemptedTaskMaxRunningTime = newPreemptedTaskMaxRunningTime;
this.preemptionRounds = newPreemptionRounds;
this.scheduleFromNodeToSession = newScheduleFromNodeToSession;
this.poolInfoToRedirect = newPoolInfoToRedirect;
}
}
/**
* Reload the general configuration and update all in-memory values. Should
* be invoked under synchronization.
*
* @throws IOException
* @throws SAXException
* @throws ParserConfigurationException
*/
private void reloadConfig() throws
IOException, SAXException, ParserConfigurationException, JSONException {
// Loading corona configuration as JSON.
if (configFileName != null && configFileName.endsWith(
Configuration.MATERIALIZEDJSON)) {
reloadJsonConfig();
return;
}
// Loading corona configuration as XML. XML configurations are
// deprecated. We intend to remove all XML configurations and
// transition entirely into JSON.
Map<ResourceType, Long> newTypeToNodeWait;
Map<ResourceType, Long> newTypeToRackWait;
ScheduleComparator newDefaultPoolComparator = DEFAULT_POOL_COMPARATOR;
double newShareStarvingRatio = DEFAULT_SHARE_STARVING_RATIO;
long newMinPreemptPeriod = DEFAULT_MIN_PREEMPT_PERIOD;
int newGrantsPerIteration = DEFAULT_GRANTS_PER_ITERATION;
long newStarvingTimeForMinimum = DEFAULT_STARVING_TIME_FOR_MINIMUM;
long newStarvingTimeForShare = DEFAULT_STARVING_TIME_FOR_SHARE;
long newPreemptedTaskMaxRunningTime = DEFAULT_PREEMPT_TASK_MAX_RUNNING_TIME;
int newPreemptionRounds = DEFAULT_PREEMPTION_ROUNDS;
boolean newScheduleFromNodeToSession = DEFAULT_SCHEDULE_FROM_NODE_TO_SESSION;
newTypeToNodeWait = new EnumMap<ResourceType, Long>(ResourceType.class);
newTypeToRackWait = new EnumMap<ResourceType, Long>(ResourceType.class);
Map<PoolInfo, PoolInfo> newPoolInfoToRedirect =
new HashMap<PoolInfo, PoolInfo>();
for (ResourceType type : TYPES) {
newTypeToNodeWait.put(type, 0L);
newTypeToRackWait.put(type, 0L);
}
Element root = getRootElement(configFileName);
NodeList elements = root.getChildNodes();
for (int i = 0; i < elements.getLength(); ++i) {
Node node = elements.item(i);
if (!(node instanceof Element)) {
continue;
}
Element element = (Element) node;
for (ResourceType type : TYPES) {
if (matched(element, "nodeLocalityWait" + type)) {
long val = Long.parseLong(getText(element));
newTypeToNodeWait.put(type, val);
}
if (matched(element, "rackLocalityWait" + type)) {
long val = Long.parseLong(getText(element));
newTypeToRackWait.put(type, val);
}
}
if (matched(element, "defaultSchedulingMode")) {
newDefaultPoolComparator =
ScheduleComparator.valueOf(getText(element));
}
if (matched(element, "shareStarvingRatio")) {
newShareStarvingRatio = Double.parseDouble(getText(element));
if (newShareStarvingRatio < 0 || newShareStarvingRatio > 1.0) {
LOG.error("Illegal shareStarvingRatio:" + newShareStarvingRatio);
newShareStarvingRatio = DEFAULT_SHARE_STARVING_RATIO;
}
}
if (matched(element, "grantsPerIteration")) {
newGrantsPerIteration = Integer.parseInt(getText(element));
if (newMinPreemptPeriod < 0) {
LOG.error("Illegal grantsPerIteration: " + newGrantsPerIteration);
newGrantsPerIteration = DEFAULT_GRANTS_PER_ITERATION;
}
}
if (matched(element, "minPreemptPeriod")) {
newMinPreemptPeriod = Long.parseLong(getText(element));
if (newMinPreemptPeriod < 0) {
LOG.error("Illegal minPreemptPeriod: " + newMinPreemptPeriod);
newMinPreemptPeriod = DEFAULT_MIN_PREEMPT_PERIOD;
}
}
if (matched(element, "starvingTimeForShare")) {
newStarvingTimeForShare = Long.parseLong(getText(element));
if (newStarvingTimeForShare < 0) {
LOG.error("Illegal starvingTimeForShare:" + newStarvingTimeForShare);
newStarvingTimeForShare = DEFAULT_STARVING_TIME_FOR_SHARE;
}
}
if (matched(element, "starvingTimeForMinimum")) {
newStarvingTimeForMinimum = Long.parseLong(getText(element));
if (newStarvingTimeForMinimum < 0) {
LOG.error("Illegal starvingTimeForMinimum:" +
newStarvingTimeForMinimum);
newStarvingTimeForMinimum = DEFAULT_STARVING_TIME_FOR_MINIMUM;
}
}
if (matched(element, "preemptedTaskMaxRunningTime")) {
newPreemptedTaskMaxRunningTime = Long.parseLong(getText(element));
if (newPreemptedTaskMaxRunningTime < 0) {
LOG.error("Illegal preemptedTaskMaxRunningTime:" +
newPreemptedTaskMaxRunningTime);
newPreemptedTaskMaxRunningTime =
DEFAULT_PREEMPT_TASK_MAX_RUNNING_TIME;
}
}
if (matched(element, "preemptionRounds")) {
newPreemptionRounds = Integer.parseInt(getText(element));
if (newPreemptionRounds < 0) {
LOG.error("Illegal preemptedTaskMaxRunningTime:" +
newPreemptionRounds);
newPreemptionRounds = DEFAULT_PREEMPTION_ROUNDS;
}
}
if (matched(element, "scheduleFromNodeToSession")) {
newScheduleFromNodeToSession = Boolean.parseBoolean(getText(element));
}
if (matched(element, REDIRECT_TAG_NAME)) {
loadPoolInfoToRedirect(
element.getAttribute(SOURCE_ATTRIBUTE),
element.getAttribute(DESTINATION_ATTRIBUTE),
newPoolInfoToRedirect);
}
}
synchronized (this) {
this.typeToNodeWait = newTypeToNodeWait;
this.typeToRackWait = newTypeToRackWait;
this.defaultPoolComparator = newDefaultPoolComparator;
this.shareStarvingRatio = newShareStarvingRatio;
this.minPreemptPeriod = newMinPreemptPeriod;
this.grantsPerIteration = newGrantsPerIteration;
this.starvingTimeForMinimum = newStarvingTimeForMinimum;
this.starvingTimeForShare = newStarvingTimeForShare;
this.preemptedTaskMaxRunningTime = newPreemptedTaskMaxRunningTime;
this.preemptionRounds = newPreemptionRounds;
this.scheduleFromNodeToSession = newScheduleFromNodeToSession;
this.poolInfoToRedirect = newPoolInfoToRedirect;
}
}
/**
* Reload the pools config and update all in-memory values
* @throws ParserConfigurationException
* @throws SAXException
* @throws IOException
*/
private void reloadPoolsConfig()
throws IOException, SAXException, ParserConfigurationException {
if (poolsConfigFileName == null) {
return;
}
Set<String> newPoolGroupNameSet = new HashSet<String>();
Set<PoolInfo> newPoolInfoSet = new HashSet<PoolInfo>();
TypePoolGroupNameMap<Integer> newTypePoolNameGroupToMax =
new TypePoolGroupNameMap<Integer>();
TypePoolGroupNameMap<Integer> newTypePoolNameGroupToMin =
new TypePoolGroupNameMap<Integer>();
TypePoolInfoMap<Integer> newTypePoolInfoToMax =
new TypePoolInfoMap<Integer>();
TypePoolInfoMap<Integer> newTypePoolInfoToMin =
new TypePoolInfoMap<Integer>();
Map<PoolInfo, ScheduleComparator> newPoolInfoToComparator =
new HashMap<PoolInfo, ScheduleComparator>();
Map<PoolInfo, Double> newPoolInfoToWeight =
new HashMap<PoolInfo, Double>();
Map<PoolInfo, Integer> newPoolInfoToPriority =
new HashMap<PoolInfo, Integer>();
Map<PoolInfo, String> newPoolInfoToWhitelist =
new HashMap<PoolInfo, String>();
Map<PoolInfo, PoolInfo> newJobExceedsLimitPoolRedirect = new HashMap<PoolInfo, PoolInfo>();
Map<PoolInfo, Long> newPoolJobSizeLimit = new HashMap<PoolInfo, Long>();
Element root = getRootElement(poolsConfigFileName);
NodeList elements = root.getChildNodes();
for (int i = 0; i < elements.getLength(); ++i) {
Node node = elements.item(i);
if (!(node instanceof Element)) {
continue;
}
Element element = (Element) node;
if (matched(element, GROUP_TAG_NAME)) {
String groupName = element.getAttribute(NAME_ATTRIBUTE);
if (!newPoolGroupNameSet.add(groupName)) {
LOG.debug("Already added group " + groupName);
}
NodeList groupFields = element.getChildNodes();
for (int j = 0; j < groupFields.getLength(); ++j) {
Node groupNode = groupFields.item(j);
if (!(groupNode instanceof Element)) {
continue;
}
Element field = (Element) groupNode;
for (ResourceType type : TYPES) {
if (matched(field, MIN_TAG_NAME_PREFIX + type)) {
int val = Integer.parseInt(getText(field));
newTypePoolNameGroupToMin.put(type, groupName, val);
}
if (matched(field, MAX_TAG_NAME_PREFIX + type)) {
int val = Integer.parseInt(getText(field));
newTypePoolNameGroupToMax.put(type, groupName, val);
}
}
if (matched(field,REDIRECT_JOB_WITH_LIMIT)){
loadPoolInfoToRedirect(groupName,
field.getAttribute(SOURCE_ATTRIBUTE),
field.getAttribute(DESTINATION_ATTRIBUTE),
field.getAttribute(JOB_INPUT_SIZE_LIMIT_ATTRIBUTE),
newJobExceedsLimitPoolRedirect,
newPoolJobSizeLimit);
}
if (matched(field, POOL_TAG_NAME)) {
PoolInfo poolInfo = new PoolInfo(groupName,
field.getAttribute("name"));
if (!newPoolInfoSet.add(poolInfo)) {
LOG.warn("Already added pool info " + poolInfo);
}
NodeList poolFields = field.getChildNodes();
for (int k = 0; k < poolFields.getLength(); ++k) {
Node poolNode = poolFields.item(k);
if (!(poolNode instanceof Element)) {
continue;
}
Element poolField = (Element) poolNode;
for (ResourceType type : TYPES) {
if (matched(poolField, MIN_TAG_NAME_PREFIX + type)) {
int val = Integer.parseInt(getText(poolField));
newTypePoolInfoToMin.put(type, poolInfo, val);
}
if (matched(poolField, MAX_TAG_NAME_PREFIX + type)) {
int val = Integer.parseInt(getText(poolField));
newTypePoolInfoToMax.put(type, poolInfo, val);
}
}
if (matched(poolField, PREEMPTABILITY_MODE_TAG_NAME)) {
boolean val = Boolean.parseBoolean(getText(poolField));
if (!val) {
nonPreemptablePools.add(poolInfo);
}
}
if (matched(poolField, REQUEST_MAX_MODE_TAG_NAME)) {
boolean val = Boolean.parseBoolean(getText(poolField));
if (val) {
requestMaxPools.add(poolInfo);
}
}
if (matched(poolField, SCHEDULING_MODE_TAG_NAME)) {
ScheduleComparator val =
ScheduleComparator.valueOf(getText(poolField));
newPoolInfoToComparator.put(poolInfo, val);
}
if (matched(poolField, WEIGHT_TAG_NAME)) {
double val = Double.parseDouble(getText(poolField));
newPoolInfoToWeight.put(poolInfo, val);
}
if (matched(poolField, PRIORITY_TAG_NAME)) {
int val = Integer.parseInt(getText(poolField));
newPoolInfoToPriority.put(poolInfo, val);
}
if (matched(poolField, WHITELIST_TAG_NAME)) {
newPoolInfoToWhitelist.put(poolInfo, getText(poolField));
}
}
}
}
}
}
synchronized (this) {
this.poolGroupNameSet = newPoolGroupNameSet;
this.poolInfoSet = Collections.unmodifiableSet(newPoolInfoSet);
this.typePoolGroupNameToMax = newTypePoolNameGroupToMax;
this.typePoolGroupNameToMin = newTypePoolNameGroupToMin;
this.typePoolInfoToMax = newTypePoolInfoToMax;
this.typePoolInfoToMin = newTypePoolInfoToMin;
this.poolInfoToComparator = newPoolInfoToComparator;
this.poolInfoToWeight = newPoolInfoToWeight;
this.poolInfoToPriority = newPoolInfoToPriority;
this.poolInfoToWhitelist = newPoolInfoToWhitelist;
this.jobExceedsLimitPoolRedirect = newJobExceedsLimitPoolRedirect;
this.poolJobSizeLimit = newPoolJobSizeLimit;
}
}
/**
* Reload all the configuration files if the config changed and
* set the last successful reload time. Synchronized due to potential
* conflict from a fetch pools config http request.
*
* @return true if the config was reloaded, false otherwise
* @throws IOException
* @throws SAXException
* @throws ParserConfigurationException
* @param init true when the config manager is being initialized.
* false on reloads
*/
public synchronized boolean reloadAllConfig(boolean init)
throws IOException, SAXException, ParserConfigurationException, JSONException {
if (!isConfigChanged(init)) {
return false;
}
reloadConfig();
reloadPoolsConfig();
this.lastSuccessfulReload = ClusterManager.clock.getTime();
return true;
}
/**
* Check if the config files have changed since they were last read
* @return true if the modification time of the file is greater
* than that of the last successful reload, false otherwise
* @param init true when the config manager is being initialized.
* false on reloads
*/
private boolean isConfigChanged(boolean init)
throws IOException {
if (init &&
(configFileName == null ||
(poolsConfigFileName == null &&
conf.onlyAllowConfiguredPools()))) {
throw new IOException("ClusterManager needs a config and a " +
"pools file to start");
}
if (configFileName == null && poolsConfigFileName == null) {
return false;
}
boolean configChanged = false;
if (configFileName != null) {
File file = new File(configFileName);
configChanged |= (file.lastModified() == 0 ||
file.lastModified() > lastSuccessfulReload);
}
if (poolsConfigFileName != null) {
File file = new File(poolsConfigFileName);
configChanged |= (file.lastModified() == 0 ||
file.lastModified() > lastSuccessfulReload);
}
return configChanged;
}
/**
* Get the root element of the XML document
* @return the root element of the XML document
* @throws IOException
* @throws SAXException
* @throws ParserConfigurationException
*/
private Element getRootElement(String fileName) throws
IOException, SAXException, ParserConfigurationException {
DocumentBuilderFactory docBuilderFactory =
DocumentBuilderFactory.newInstance();
docBuilderFactory.setIgnoringComments(true);
DocumentBuilder builder = docBuilderFactory.newDocumentBuilder();
Document doc = builder.parse(new File(fileName));
Element root = doc.getDocumentElement();
if (!matched(root, CONFIGURATION_TAG_NAME)) {
throw new IOException("Bad " + fileName);
}
return root;
}
/**
* Check if the element name matches the tagName provided
* @param element the xml element
* @param tagName the name to check against
* @return true if the name of the element matches tagName, false otherwise
*/
private static boolean matched(Element element, String tagName) {
return tagName.equals(element.getTagName());
}
/**
* Get the text inside of the Xml element
* @param element xml element
* @return the text inside of the xml element
*/
private static String getText(Element element) {
if (element.getFirstChild() == null) {
return "";
}
return ((Text) element.getFirstChild()).getData().trim();
}
}