org.apache.hadoop.conf.ReconfigurableBase Maven / Gradle / Ivy
/**
* 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.conf;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import org.apache.commons.logging.*;
import org.apache.hadoop.util.Time;
import org.apache.hadoop.conf.ReconfigurationUtil.PropertyChange;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
/**
* Utility base class for implementing the Reconfigurable interface.
*
* Subclasses should override reconfigurePropertyImpl to change individual
* properties and getReconfigurableProperties to get all properties that
* can be changed at run time.
*/
public abstract class ReconfigurableBase
extends Configured implements Reconfigurable {
private static final Log LOG =
LogFactory.getLog(ReconfigurableBase.class);
// Use for testing purpose.
private ReconfigurationUtil reconfigurationUtil = new ReconfigurationUtil();
/** Background thread to reload configuration. */
private Thread reconfigThread = null;
private volatile boolean shouldRun = true;
private Object reconfigLock = new Object();
/**
* The timestamp when the reconfigThread
starts.
*/
private long startTime = 0;
/**
* The timestamp when the reconfigThread
finishes.
*/
private long endTime = 0;
/**
* A map of . If error message is present,
* it contains the messages about the error occurred when applies the particular
* change. Otherwise, it indicates that the change has been successfully applied.
*/
private Map> status = null;
/**
* Construct a ReconfigurableBase.
*/
public ReconfigurableBase() {
super(new Configuration());
}
/**
* Construct a ReconfigurableBase with the {@link Configuration}
* conf.
*/
public ReconfigurableBase(Configuration conf) {
super((conf == null) ? new Configuration() : conf);
}
@VisibleForTesting
public void setReconfigurationUtil(ReconfigurationUtil ru) {
reconfigurationUtil = Preconditions.checkNotNull(ru);
}
@VisibleForTesting
public Collection getChangedProperties(
Configuration newConf, Configuration oldConf) {
return reconfigurationUtil.parseChangedProperties(newConf, oldConf);
}
/**
* A background thread to apply configuration changes.
*/
private static class ReconfigurationThread extends Thread {
private ReconfigurableBase parent;
ReconfigurationThread(ReconfigurableBase base) {
this.parent = base;
}
// See {@link ReconfigurationServlet#applyChanges}
public void run() {
LOG.info("Starting reconfiguration task.");
Configuration oldConf = this.parent.getConf();
Configuration newConf = new Configuration();
Collection changes =
this.parent.getChangedProperties(newConf, oldConf);
Map> results = Maps.newHashMap();
ConfigRedactor oldRedactor = new ConfigRedactor(oldConf);
ConfigRedactor newRedactor = new ConfigRedactor(newConf);
for (PropertyChange change : changes) {
String errorMessage = null;
String oldValRedacted = oldRedactor.redact(change.prop, change.oldVal);
String newValRedacted = newRedactor.redact(change.prop, change.newVal);
if (!this.parent.isPropertyReconfigurable(change.prop)) {
errorMessage = "Property " + change.prop +
" is not reconfigurable";
LOG.info(errorMessage);
results.put(change, Optional.of(errorMessage));
continue;
}
LOG.info("Change property: " + change.prop + " from \""
+ ((change.oldVal == null) ? "" : oldValRedacted)
+ "\" to \""
+ ((change.newVal == null) ? "" : newValRedacted)
+ "\".");
try {
this.parent.reconfigurePropertyImpl(change.prop, change.newVal);
} catch (ReconfigurationException e) {
errorMessage = e.getCause().getMessage();
}
results.put(change, Optional.fromNullable(errorMessage));
}
synchronized (this.parent.reconfigLock) {
this.parent.endTime = Time.now();
this.parent.status = Collections.unmodifiableMap(results);
this.parent.reconfigThread = null;
}
}
}
/**
* Start a reconfiguration task to reload configuration in background.
*/
public void startReconfigurationTask() throws IOException {
synchronized (reconfigLock) {
if (!shouldRun) {
String errorMessage = "The server is stopped.";
LOG.warn(errorMessage);
throw new IOException(errorMessage);
}
if (reconfigThread != null) {
String errorMessage = "Another reconfiguration task is running.";
LOG.warn(errorMessage);
throw new IOException(errorMessage);
}
reconfigThread = new ReconfigurationThread(this);
reconfigThread.setDaemon(true);
reconfigThread.setName("Reconfiguration Task");
reconfigThread.start();
startTime = Time.now();
}
}
public ReconfigurationTaskStatus getReconfigurationTaskStatus() {
synchronized (reconfigLock) {
if (reconfigThread != null) {
return new ReconfigurationTaskStatus(startTime, 0, null);
}
return new ReconfigurationTaskStatus(startTime, endTime, status);
}
}
public void shutdownReconfigurationTask() {
Thread tempThread;
synchronized (reconfigLock) {
shouldRun = false;
if (reconfigThread == null) {
return;
}
tempThread = reconfigThread;
reconfigThread = null;
}
try {
tempThread.join();
} catch (InterruptedException e) {
}
}
/**
* {@inheritDoc}
*
* This method makes the change to this objects {@link Configuration}
* and calls reconfigurePropertyImpl to update internal data structures.
* This method cannot be overridden, subclasses should instead override
* reconfigureProperty.
*/
@Override
public final String reconfigureProperty(String property, String newVal)
throws ReconfigurationException {
if (isPropertyReconfigurable(property)) {
LOG.info("changing property " + property + " to " + newVal);
String oldVal;
synchronized(getConf()) {
oldVal = getConf().get(property);
reconfigurePropertyImpl(property, newVal);
if (newVal != null) {
getConf().set(property, newVal);
} else {
getConf().unset(property);
}
}
return oldVal;
} else {
throw new ReconfigurationException(property, newVal,
getConf().get(property));
}
}
/**
* {@inheritDoc}
*
* Subclasses must override this.
*/
@Override
public abstract Collection getReconfigurableProperties();
/**
* {@inheritDoc}
*
* Subclasses may wish to override this with a more efficient implementation.
*/
@Override
public boolean isPropertyReconfigurable(String property) {
return getReconfigurableProperties().contains(property);
}
/**
* Change a configuration property.
*
* Subclasses must override this. This method applies the change to
* all internal data structures derived from the configuration property
* that is being changed. If this object owns other Reconfigurable objects
* reconfigureProperty should be called recursively to make sure that
* to make sure that the configuration of these objects is updated.
*/
protected abstract void reconfigurePropertyImpl(String property, String newVal)
throws ReconfigurationException;
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy