org.apache.calcite.avatica.ConnectionConfigImpl 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.calcite.avatica;
import org.apache.calcite.avatica.remote.Service;
import java.lang.reflect.Field;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
/** Implementation of {@link ConnectionConfig}. */
public class ConnectionConfigImpl implements ConnectionConfig {
protected final Properties properties;
public ConnectionConfigImpl(Properties properties) {
this.properties = properties;
}
public String schema() {
return BuiltInConnectionProperty.SCHEMA.wrap(properties).getString();
}
public String timeZone() {
return BuiltInConnectionProperty.TIMEZONE.wrap(properties).getString();
}
public Service.Factory factory() {
return BuiltInConnectionProperty.FACTORY.wrap(properties)
.getPlugin(Service.Factory.class, null);
}
public String url() {
return BuiltInConnectionProperty.URL.wrap(properties).getString();
}
/** Converts a {@link Properties} object containing (name, value)
* pairs into a map whose keys are
* {@link org.apache.calcite.avatica.InternalProperty} objects.
*
* Matching is case-insensitive. Throws if a property is not known.
* If a property occurs more than once, takes the last occurrence.
*
* @param properties Properties
* @return Map
* @throws RuntimeException if a property is not known
*/
public static Map parse(Properties properties,
Map nameToProps) {
final Map map =
new LinkedHashMap();
for (String name : properties.stringPropertyNames()) {
final ConnectionProperty connectionProperty =
nameToProps.get(name.toUpperCase());
if (connectionProperty == null) {
// For now, don't throw. It messes up sub-projects.
//throw new RuntimeException("Unknown property '" + name + "'");
continue;
}
map.put(connectionProperty, properties.getProperty(name));
}
return map;
}
/** The combination of a property definition and a map of property values. */
public static class PropEnv {
final Map extends ConnectionProperty, String> map;
private final ConnectionProperty property;
public PropEnv(Map extends ConnectionProperty, String> map,
ConnectionProperty property) {
this.map = map;
this.property = property;
}
private T get_(Converter converter, String defaultValue) {
final String s = map.get(property);
if (s != null) {
return converter.apply(property, s);
}
return converter.apply(property, defaultValue);
}
/** Returns the string value of this property, or null if not specified and
* no default. */
public String getString() {
return getString((String) property.defaultValue());
}
/** Returns the string value of this property, or null if not specified and
* no default. */
public String getString(String defaultValue) {
assert property.type() == ConnectionProperty.Type.STRING;
return get_(IDENTITY_CONVERTER, defaultValue);
}
/** Returns the boolean value of this property. Throws if not set and no
* default. */
public boolean getBoolean() {
return getBoolean((Boolean) property.defaultValue());
}
/** Returns the boolean value of this property. Throws if not set and no
* default. */
public boolean getBoolean(boolean defaultValue) {
assert property.type() == ConnectionProperty.Type.BOOLEAN;
return get_(BOOLEAN_CONVERTER, Boolean.toString(defaultValue));
}
/** Returns the enum value of this property. Throws if not set and no
* default. */
public > E getEnum(Class enumClass) {
//noinspection unchecked
return getEnum(enumClass, (E) property.defaultValue());
}
/** Returns the enum value of this property. Throws if not set and no
* default. */
public > E getEnum(Class enumClass, E defaultValue) {
assert property.type() == ConnectionProperty.Type.ENUM;
//noinspection unchecked
return get_(enumConverter(enumClass), defaultValue.name());
}
/** Returns an instance of a plugin.
*
* Throws if not set and no default.
* Also throws if the class does not implement the required interface,
* or if it does not have a public default constructor or an public static
* field called {@code #INSTANCE}. */
public T getPlugin(Class pluginClass, T defaultInstance) {
return getPlugin(pluginClass, (String) property.defaultValue(),
defaultInstance);
}
/** Returns an instance of a plugin, using a given class name if none is
* set.
*
* Throws if not set and no default.
* Also throws if the class does not implement the required interface,
* or if it does not have a public default constructor or an public static
* field called {@code #INSTANCE}. */
public T getPlugin(Class pluginClass, String defaultClassName,
T defaultInstance) {
assert property.type() == ConnectionProperty.Type.PLUGIN;
return get_(pluginConverter(pluginClass, defaultInstance),
defaultClassName);
}
}
/** Callback to parse a property from string to its native type. */
public interface Converter {
T apply(ConnectionProperty connectionProperty, String s);
}
public static final Converter BOOLEAN_CONVERTER =
new Converter() {
public Boolean apply(ConnectionProperty connectionProperty, String s) {
if (s == null) {
throw new RuntimeException("Required property '"
+ connectionProperty.camelName() + "' not specified");
}
return Boolean.parseBoolean(s);
}
};
public static final Converter IDENTITY_CONVERTER =
new Converter() {
public String apply(ConnectionProperty connectionProperty, String s) {
return s;
}
};
public static Converter enumConverter(
final Class enumClass) {
return new Converter() {
public E apply(ConnectionProperty connectionProperty, String s) {
if (s == null) {
throw new RuntimeException("Required property '"
+ connectionProperty.camelName() + "' not specified");
}
try {
return (E) Enum.valueOf(enumClass, s);
} catch (IllegalArgumentException e) {
throw new RuntimeException("Property '" + s + "' not valid for enum "
+ enumClass.getName());
}
}
};
}
public static Converter pluginConverter(final Class pluginClass,
final T defaultInstance) {
return new Converter() {
public T apply(ConnectionProperty connectionProperty, String s) {
if (s == null) {
if (defaultInstance != null) {
return defaultInstance;
}
if (!connectionProperty.required()) {
return null;
}
throw new RuntimeException("Required property '"
+ connectionProperty.camelName() + "' not specified");
}
// First look for a C.INSTANCE field, then do new C().
try {
//noinspection unchecked
final Class clazz = (Class) Class.forName(s);
assert pluginClass.isAssignableFrom(clazz);
try {
// We assume that if there is an INSTANCE field it is static and
// has the right type.
final Field field = clazz.getField("INSTANCE");
return pluginClass.cast(field.get(null));
} catch (NoSuchFieldException e) {
// ignore
}
return clazz.newInstance();
} catch (Exception e) {
throw new RuntimeException("Property '" + s
+ "' not valid for plugin type " + pluginClass.getName(), e);
}
}
};
}
}
// End ConnectionConfigImpl.java