| /* |
| * 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.AvaticaHttpClientFactory; |
| import org.apache.calcite.avatica.remote.HostnameVerificationConfigurable.HostnameVerification; |
| import org.apache.calcite.avatica.remote.Service; |
| |
| import java.io.File; |
| import java.math.BigDecimal; |
| import java.util.LinkedHashMap; |
| import java.util.Locale; |
| 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.TIME_ZONE.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(); |
| } |
| |
| public String serialization() { |
| return BuiltInConnectionProperty.SERIALIZATION.wrap(properties).getString(); |
| } |
| |
| public String authentication() { |
| return BuiltInConnectionProperty.AUTHENTICATION.wrap(properties).getString(); |
| } |
| |
| public String avaticaUser() { |
| return BuiltInConnectionProperty.AVATICA_USER.wrap(properties).getString(); |
| } |
| |
| public String avaticaPassword() { |
| return BuiltInConnectionProperty.AVATICA_PASSWORD.wrap(properties).getString(); |
| } |
| |
| public AvaticaHttpClientFactory httpClientFactory() { |
| return BuiltInConnectionProperty.HTTP_CLIENT_FACTORY.wrap(properties) |
| .getPlugin(AvaticaHttpClientFactory.class, null); |
| } |
| |
| public String httpClientClass() { |
| return BuiltInConnectionProperty.HTTP_CLIENT_IMPL.wrap(properties).getString(); |
| } |
| |
| public String kerberosPrincipal() { |
| return BuiltInConnectionProperty.PRINCIPAL.wrap(properties).getString(); |
| } |
| |
| public File kerberosKeytab() { |
| String keytabPath = BuiltInConnectionProperty.KEYTAB.wrap(properties).getString(); |
| if (null == keytabPath) { |
| return null; |
| } |
| File keytab = new File(keytabPath); |
| if (!keytab.exists() || !keytab.isFile()) { |
| throw new RuntimeException("The " + BuiltInConnectionProperty.KEYTAB.name() + " does not " |
| + " reference a normal, existent file: " + keytabPath); |
| } |
| return keytab; |
| } |
| |
| public File truststore() { |
| String filename = BuiltInConnectionProperty.TRUSTSTORE.wrap(properties).getString(); |
| if (null == filename) { |
| return null; |
| } |
| return new File(filename); |
| } |
| |
| public String truststorePassword() { |
| return BuiltInConnectionProperty.TRUSTSTORE_PASSWORD.wrap(properties).getString(); |
| } |
| |
| public File keystore() { |
| String filename = BuiltInConnectionProperty.KEYSTORE.wrap(properties).getString(); |
| if (null == filename) { |
| return null; |
| } |
| return new File(filename); |
| } |
| |
| public String keystorePassword() { |
| return BuiltInConnectionProperty.KEYSTORE_PASSWORD.wrap(properties).getString(); |
| } |
| |
| public String keyPassword() { |
| return BuiltInConnectionProperty.KEY_PASSWORD.wrap(properties).getString(); |
| |
| } |
| |
| public HostnameVerification hostnameVerification() { |
| return BuiltInConnectionProperty.HOSTNAME_VERIFICATION.wrap(properties) |
| .getEnum(HostnameVerification.class); |
| } |
| |
| /** Converts a {@link Properties} object containing (name, value) |
| * pairs into a map whose keys are |
| * {@link org.apache.calcite.avatica.InternalProperty} objects. |
| * |
| * <p>Matching is case-insensitive. Throws if a property is not known. |
| * If a property occurs more than once, takes the last occurrence.</p> |
| * |
| * @param properties Properties |
| * @return Map |
| * @throws RuntimeException if a property is not known |
| */ |
| public static Map<ConnectionProperty, String> parse(Properties properties, |
| Map<String, ? extends ConnectionProperty> nameToProps) { |
| final Map<ConnectionProperty, String> map = |
| new LinkedHashMap<ConnectionProperty, String>(); |
| for (String name : properties.stringPropertyNames()) { |
| final ConnectionProperty connectionProperty = |
| nameToProps.get(name.toUpperCase(Locale.ROOT)); |
| 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> T get_(Converter<T> converter, String defaultValue) { |
| final String s = map.get(property); |
| if (s != null) { |
| return converter.apply(property, s); |
| } |
| return converter.apply(property, defaultValue); |
| } |
| |
| private <T> T getDefaultNull(Converter<T> converter) { |
| final String s = map.get(property); |
| if (s != null) { |
| return converter.apply(property, s); |
| } |
| return null; |
| } |
| |
| /** 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 int value of this property. Throws if not set and no |
| * default. */ |
| public int getInt() { |
| return getInt((Number) property.defaultValue()); |
| } |
| |
| /** Returns the int value of this property. Throws if not set and no |
| * default. */ |
| public int getInt(Number defaultValue) { |
| assert property.type() == ConnectionProperty.Type.NUMBER; |
| return get_(NUMBER_CONVERTER, defaultValue.toString()).intValue(); |
| } |
| |
| /** Returns the long value of this property. Throws if not set and no |
| * default. */ |
| public long getLong() { |
| return getLong((Number) property.defaultValue()); |
| } |
| |
| /** Returns the long value of this property. Throws if not set and no |
| * default. */ |
| public long getLong(Number defaultValue) { |
| assert property.type() == ConnectionProperty.Type.NUMBER; |
| return get_(NUMBER_CONVERTER, defaultValue.toString()).longValue(); |
| } |
| |
| /** Returns the double value of this property. Throws if not set and no |
| * default. */ |
| public double getDouble() { |
| return getDouble((Number) property.defaultValue()); |
| } |
| |
| /** Returns the double value of this property. Throws if not set and no |
| * default. */ |
| public double getDouble(Number defaultValue) { |
| assert property.type() == ConnectionProperty.Type.NUMBER; |
| return get_(NUMBER_CONVERTER, defaultValue.toString()).doubleValue(); |
| } |
| |
| /** 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 extends Enum<E>> E getEnum(Class<E> 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 extends Enum<E>> E getEnum(Class<E> enumClass, E defaultValue) { |
| if (property.type() != ConnectionProperty.Type.ENUM) { |
| throw new AssertionError("not an enum"); |
| } |
| if (enumClass != property.valueClass()) { |
| throw new AssertionError("wrong value class; expected " |
| + property.valueClass()); |
| } |
| if (defaultValue == null) { |
| return getDefaultNull(enumConverter(enumClass)); |
| } else { |
| return get_(enumConverter(enumClass), defaultValue.name()); |
| } |
| } |
| |
| /** Returns an instance of a plugin. |
| * |
| * <p>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> T getPlugin(Class<T> 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. |
| * |
| * <p>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> T getPlugin(Class<T> 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. |
| * @param <T> the native type */ |
| public interface Converter<T> { |
| T apply(ConnectionProperty connectionProperty, String s); |
| } |
| |
| public static final Converter<Boolean> BOOLEAN_CONVERTER = |
| new Converter<Boolean>() { |
| public Boolean apply(ConnectionProperty connectionProperty, String s) { |
| if (s == null) { |
| throw new RuntimeException("Required property '" |
| + connectionProperty.camelName() + "' not specified"); |
| } |
| return Boolean.parseBoolean(s); |
| } |
| }; |
| |
| static final Map<String, BigDecimal> MULTIPLIER_MAP = |
| new LinkedHashMap<>(); |
| static { |
| MULTIPLIER_MAP.put("k", new BigDecimal(1024)); |
| MULTIPLIER_MAP.put("K", new BigDecimal(1024)); |
| MULTIPLIER_MAP.put("m", new BigDecimal(1024 * 1024)); |
| MULTIPLIER_MAP.put("M", new BigDecimal(1024 * 1024)); |
| MULTIPLIER_MAP.put("g", new BigDecimal(1024 * 1024 * 1024)); |
| MULTIPLIER_MAP.put("G", new BigDecimal(1024 * 1024 * 1024)); |
| } |
| |
| public static final Converter<Number> NUMBER_CONVERTER = |
| new Converter<Number>() { |
| public Number apply(ConnectionProperty connectionProperty, String s) { |
| if (s == null) { |
| throw new RuntimeException("Required property '" |
| + connectionProperty.camelName() + "' not specified"); |
| } |
| BigDecimal multiplier = BigDecimal.ONE; |
| for (Map.Entry<String, BigDecimal> e : MULTIPLIER_MAP.entrySet()) { |
| if (s.endsWith(e.getKey())) { |
| multiplier = e.getValue(); |
| s = s.substring(0, s.length() - e.getKey().length()); |
| } |
| } |
| return new BigDecimal(s).multiply(multiplier); |
| } |
| }; |
| |
| public static final Converter<String> IDENTITY_CONVERTER = |
| new Converter<String>() { |
| public String apply(ConnectionProperty connectionProperty, String s) { |
| return s; |
| } |
| }; |
| |
| public static <E extends Enum> Converter<E> enumConverter( |
| final Class<E> enumClass) { |
| return new Converter<E>() { |
| 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) { |
| // Case insensitive match is OK too. |
| for (E c : enumClass.getEnumConstants()) { |
| if (c.name().equalsIgnoreCase(s)) { |
| return c; |
| } |
| } |
| throw new RuntimeException("Property '" + s + "' not valid for enum " |
| + enumClass.getName()); |
| } |
| } |
| }; |
| } |
| |
| public static <T> Converter<T> pluginConverter(final Class<T> pluginClass, |
| final T defaultInstance) { |
| return new Converter<T>() { |
| 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"); |
| } |
| return AvaticaUtils.instantiatePlugin(pluginClass, s); |
| } |
| }; |
| } |
| } |
| |
| // End ConnectionConfigImpl.java |