HBASE-19295 The Configuration returned by CPEnv should be read-only.

Adds a ReadOnlyConfiguration that delegates gets but throws exception
on sets/adds, etc.
This commit is contained in:
Michael Stack 2017-12-04 20:58:02 -08:00
parent 497902731a
commit d29ffd4e29
4 changed files with 472 additions and 3 deletions

View File

@ -45,7 +45,10 @@ public interface CoprocessorEnvironment<C extends Coprocessor> {
/** @return the load sequence number */ /** @return the load sequence number */
int getLoadSequence(); int getLoadSequence();
/** @return the configuration */ /**
* @return a Read-only Configuration; throws {@link UnsupportedOperationException} if you try
* to set a configuration.
*/
Configuration getConfiguration(); Configuration getConfiguration();
/** /**

View File

@ -57,7 +57,7 @@ public class BaseEnvironment<C extends Coprocessor> implements CoprocessorEnviro
this.priority = priority; this.priority = priority;
this.state = Coprocessor.State.INSTALLED; this.state = Coprocessor.State.INSTALLED;
this.seq = seq; this.seq = seq;
this.conf = conf; this.conf = new ReadOnlyConfiguration(conf);
} }
/** Initialize the environment */ /** Initialize the environment */

View File

@ -0,0 +1,439 @@
/*
* 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.hbase.coprocessor;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.net.InetSocketAddress;
import java.net.URL;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.yetus.audience.InterfaceAudience;
/**
* Wraps a Configuration to make it read-only.
*/
@InterfaceAudience.Private
class ReadOnlyConfiguration extends Configuration {
private final Configuration conf;
ReadOnlyConfiguration(final Configuration conf) {
this.conf = conf;
}
@Override
public void setDeprecatedProperties() {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public void addResource(String name) {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public void addResource(URL url) {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public void addResource(Path file) {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public void addResource(InputStream in) {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public void addResource(InputStream in, String name) {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public void addResource(Configuration conf) {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public void reloadConfiguration() {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public String get(String name) {
return conf.get(name);
}
public void setAllowNullValueProperties(boolean val) {
throw new UnsupportedOperationException("Read-only Configuration");
}
public String getTrimmed(String name) {
return conf.getTrimmed(name);
}
@Override
public String getTrimmed(String name, String defaultValue) {
return conf.getTrimmed(name, defaultValue);
}
@Override
public String getRaw(String name) {
return conf.getRaw(name);
}
@Override
public void set(String name, String value) {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public void set(String name, String value, String source) {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public void unset(String name) {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public void setIfUnset(String name, String value) {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public String get(String name, String defaultValue) {
return conf.get(name, defaultValue);
}
@Override
public int getInt(String name, int defaultValue) {
return conf.getInt(name, defaultValue);
}
@Override
public int[] getInts(String name) {
return conf.getInts(name);
}
@Override
public void setInt(String name, int value) {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public long getLong(String name, long defaultValue) {
return conf.getLong(name, defaultValue);
}
@Override
public long getLongBytes(String name, long defaultValue) {
return conf.getLongBytes(name, defaultValue);
}
@Override
public void setLong(String name, long value) {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public float getFloat(String name, float defaultValue) {
return conf.getFloat(name, defaultValue);
}
@Override
public void setFloat(String name, float value) {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public double getDouble(String name, double defaultValue) {
return conf.getDouble(name, defaultValue);
}
@Override
public void setDouble(String name, double value) {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public boolean getBoolean(String name, boolean defaultValue) {
return conf.getBoolean(name, defaultValue);
}
@Override
public void setBoolean(String name, boolean value) {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public void setBooleanIfUnset(String name, boolean value) {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public <T extends Enum<T>> void setEnum(String name, T value) {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public <T extends Enum<T>> T getEnum(String name, T defaultValue) {
return conf.getEnum(name, defaultValue);
}
@Override
public void setTimeDuration(String name, long value, TimeUnit unit) {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public long getTimeDuration(String name, long defaultValue, TimeUnit unit) {
return conf.getTimeDuration(name, defaultValue, unit);
}
@Override
public Pattern getPattern(String name, Pattern defaultValue) {
return conf.getPattern(name, defaultValue);
}
@Override
public void setPattern(String name, Pattern pattern) {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public String[] getPropertySources(String name) {
return conf.getPropertySources(name);
}
@Override
public Configuration.IntegerRanges getRange(String name, String defaultValue) {
return conf.getRange(name, defaultValue);
}
@Override
public Collection<String> getStringCollection(String name) {
return conf.getStringCollection(name);
}
@Override
public String[] getStrings(String name) {
return conf.getStrings(name);
}
@Override
public String[] getStrings(String name, String... defaultValue) {
return conf.getStrings(name, defaultValue);
}
@Override
public Collection<String> getTrimmedStringCollection(String name) {
return conf.getTrimmedStringCollection(name);
}
@Override
public String[] getTrimmedStrings(String name) {
return conf.getTrimmedStrings(name);
}
@Override
public String[] getTrimmedStrings(String name, String... defaultValue) {
return conf.getTrimmedStrings(name, defaultValue);
}
@Override
public void setStrings(String name, String... values) {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public char[] getPassword(String name) throws IOException {
return conf.getPassword(name);
}
@Override
public InetSocketAddress getSocketAddr(String hostProperty, String addressProperty,
String defaultAddressValue, int defaultPort) {
return conf.getSocketAddr(hostProperty, addressProperty, defaultAddressValue, defaultPort);
}
@Override
public InetSocketAddress getSocketAddr(String name, String defaultAddress, int defaultPort) {
return conf.getSocketAddr(name, defaultAddress, defaultPort);
}
@Override
public void setSocketAddr(String name, InetSocketAddress addr) {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public InetSocketAddress updateConnectAddr(String hostProperty, String addressProperty,
String defaultAddressValue, InetSocketAddress addr) {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public InetSocketAddress updateConnectAddr(String name, InetSocketAddress addr) {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public Class<?> getClassByName(String name) throws ClassNotFoundException {
return conf.getClassByName(name);
}
@Override
public Class<?> getClassByNameOrNull(String name) {
return conf.getClassByNameOrNull(name);
}
@Override
public Class<?>[] getClasses(String name, Class<?>[] defaultValue) {
return conf.getClasses(name, defaultValue);
}
@Override
public Class<?> getClass(String name, Class<?> defaultValue) {
return conf.getClass(name, defaultValue);
}
@Override
public <U> Class<? extends U> getClass(String name, Class<? extends U> defaultValue,
Class<U> xface) {
return conf.getClass(name, defaultValue, xface);
}
@Override
public <U> List<U> getInstances(String name, Class<U> xface) {
return conf.getInstances(name, xface);
}
@Override
public void setClass(String name, Class<?> theClass, Class<?> xface) {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public Path getLocalPath(String dirsProp, String path) throws IOException {
return conf.getLocalPath(dirsProp, path);
}
@Override
public File getFile(String dirsProp, String path) throws IOException {
return conf.getFile(dirsProp, path);
}
@Override
public URL getResource(String name) {
return conf.getResource(name);
}
@Override
public InputStream getConfResourceAsInputStream(String name) {
return conf.getConfResourceAsInputStream(name);
}
@Override
public Reader getConfResourceAsReader(String name) {
return conf.getConfResourceAsReader(name);
}
@Override
public Set<String> getFinalParameters() {
return conf.getFinalParameters();
}
@Override
public int size() {
return conf.size();
}
@Override
public void clear() {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public Iterator<Map.Entry<String, String>> iterator() {
return conf.iterator();
}
@Override
public void writeXml(OutputStream out) throws IOException {
conf.writeXml(out);
}
@Override
public void writeXml(Writer out) throws IOException {
conf.writeXml(out);
}
@Override
public ClassLoader getClassLoader() {
return conf.getClassLoader();
}
@Override
public void setClassLoader(ClassLoader classLoader) {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public String toString() {
return conf.toString();
}
@Override
public void setQuietMode(boolean quietmode) {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public void readFields(DataInput in) throws IOException {
throw new UnsupportedOperationException("Read-only Configuration");
}
@Override
public void write(DataOutput out) throws IOException {
conf.write(out);
}
@Override
public Map<String, String> getValByRegex(String regex) {
return conf.getValByRegex(regex);
}
}

View File

@ -20,7 +20,6 @@
package org.apache.hadoop.hbase.coprocessor; package org.apache.hadoop.hbase.coprocessor;
import java.io.IOException; import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
@ -40,14 +39,17 @@ import org.apache.hadoop.hbase.regionserver.RegionServerCoprocessorHost;
import org.apache.hadoop.hbase.regionserver.RegionServerServices; import org.apache.hadoop.hbase.regionserver.RegionServerServices;
import org.apache.hadoop.hbase.testclassification.CoprocessorTests; import org.apache.hadoop.hbase.testclassification.CoprocessorTests;
import org.apache.hadoop.hbase.testclassification.SmallTests; import org.apache.hadoop.hbase.testclassification.SmallTests;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.experimental.categories.Category; import org.junit.experimental.categories.Category;
import org.junit.rules.ExpectedException;
/** /**
* Tests for global coprocessor loading configuration * Tests for global coprocessor loading configuration
*/ */
@Category({CoprocessorTests.class, SmallTests.class}) @Category({CoprocessorTests.class, SmallTests.class})
public class TestCoprocessorConfiguration { public class TestCoprocessorConfiguration {
@Rule public ExpectedException thrown = ExpectedException.none();
private static final Configuration CONF = HBaseConfiguration.create(); private static final Configuration CONF = HBaseConfiguration.create();
static { static {
@ -170,4 +172,29 @@ public class TestCoprocessorConfiguration {
assertFalse("Table coprocessors should not have been loaded", assertFalse("Table coprocessors should not have been loaded",
tableCoprocessorLoaded.get()); tableCoprocessorLoaded.get());
} }
/**
* Rough test that Coprocessor Environment is Read-Only.
* Just check a random CP and see that it returns a read-only config.
*/
@Test
public void testReadOnlyConfiguration() throws Exception {
Configuration conf = new Configuration(CONF);
HRegion region = mock(HRegion.class);
when(region.getRegionInfo()).thenReturn(REGIONINFO);
when(region.getTableDescriptor()).thenReturn(TABLEDESC);
RegionServerServices rsServices = mock(RegionServerServices.class);
RegionCoprocessorHost rcp = new RegionCoprocessorHost(region, rsServices, conf);
boolean found = false;
for (String cpStr: rcp.getCoprocessors()) {
CoprocessorEnvironment cpenv = rcp.findCoprocessorEnvironment(cpStr);
if (cpenv != null) {
found = true;
}
Configuration c = cpenv.getConfiguration();
thrown.expect(UnsupportedOperationException.class);
c.set("one.two.three", "four.five.six");
}
assertTrue("Should be at least one CP found", found);
}
} }