/*
* 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.drill.test;
import java.util.Collection;
import java.util.Properties;
import java.util.Map.Entry;
import org.apache.drill.common.config.DrillConfig;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigValueFactory;
/**
* Builds a {@link DrillConfig} for use in tests. Use this when a config
* is needed by itself, separate from an embedded Drillbit.
*/
public class ConfigBuilder {
protected String configResource;
protected Properties configProps;
/**
* Use the given configuration properties as overrides.
* @param configProps a collection of config properties
* @return this builder
* @see {@link #configProperty(String, Object)}
*/
public ConfigBuilder configProps(Properties configProps) {
if (hasResource()) {
// Drill provides no constructor for this use case.
throw new IllegalArgumentException( "Cannot provide both a config resource and config properties.");
}
if (this.configProps == null) {
this.configProps = configProps;
} else {
this.configProps.putAll(configProps);
}
return this;
}
/**
* Use the given configuration file, stored as a resource, to initialize
* the Drill config. Note that the resource file should have the two
* following settings to work as a config for an embedded Drillbit:
* <pre><code>
* drill.exec.sys.store.provider.local.write : false,
* drill.exec.http.enabled : false
* </code></pre>
* It may be more convenient to add your settings to the default
* config settings with {@link #configProperty(String, Object)}.
* @param configResource path to the file that contains the
* config file to be read
* @return this builder
* @see {@link #configProperty(String, Object)}
*/
public ConfigBuilder resource(String configResource) {
if (configProps != null) {
// Drill provides no constructor for this use case.
throw new IllegalArgumentException( "Cannot provide both a config resource and config properties.");
}
// TypeSafe gets unhappy about a leading slash, but other functions
// require it. Silently discard the leading slash if given to
// preserve the test writer's sanity.
this.configResource = ClusterFixture.trimSlash(configResource);
return this;
}
/**
* Add an additional boot-time property for the embedded Drillbit.
* @param key config property name
* @param value property value
* @return this builder
*/
public ConfigBuilder put(String key, Object value) {
if (hasResource()) {
// Drill provides no constructor for this use case.
throw new IllegalArgumentException( "Cannot provide both a config resource and config properties.");
}
if (configProps == null) {
configProps = new Properties();
}
configProps.put(key, value.toString());
return this;
}
public DrillConfig build() {
// Create a config
// Because of the way DrillConfig works, we can set the ZK
// connection string only if a property set is provided.
if (hasResource()) {
return DrillConfig.create(configResource);
} else if (configProps != null) {
return constructConfig();
} else {
return DrillConfig.create();
}
}
private DrillConfig constructConfig() {
Properties stringProps = new Properties();
Properties collectionProps = new Properties();
// Filter out the collection type configs and other configs which can be converted to string.
for(Entry<Object, Object> entry : configProps.entrySet()) {
if(entry.getValue() instanceof Collection<?>) {
collectionProps.put(entry.getKey(), entry.getValue());
} else {
stringProps.setProperty(entry.getKey().toString(), entry.getValue().toString());
}
}
// First create a DrillConfig based on string properties.
Config drillConfig = DrillConfig.create(stringProps);
// Then add the collection properties inside the DrillConfig. Below call to withValue returns
// a new reference. Considering mostly properties will be of string type, doing this
// later will be less expensive as compared to doing it for all the properties.
for(Entry<Object, Object> entry : collectionProps.entrySet()) {
drillConfig = drillConfig.withValue(entry.getKey().toString(),
ConfigValueFactory.fromAnyRef(entry.getValue()));
}
return new DrillConfig(drillConfig, true);
}
public boolean hasResource() {
return configResource != null;
}
}