All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.amazonaws.services.kinesis.clientlibrary.config.KinesisClientLibConfigurator Maven / Gradle / Ivy

Go to download

The Amazon Kinesis Client Library for Java enables Java developers to easily consume and process data from Amazon Kinesis.

There is a newer version: 1.15.2
Show newest version
/*
 * Copyright 2019 Amazon.com, Inc. or its affiliates.
 * Licensed 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 com.amazonaws.services.kinesis.clientlibrary.config;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.UUID;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.services.kinesis.clientlibrary.lib.worker.KinesisClientLibConfiguration;

/**
 * KinesisClientLibConfigurator constructs a KinesisClientLibConfiguration from java properties file. The following
 * three properties must be provided. 1) "applicationName" 2) "streamName" 3) "AWSCredentialsProvider"
 * KinesisClientLibConfigurator will help to automatically assign the value of "workerId" if this property is not
 * provided. In the specified properties file, any properties, which matches the variable name in
 * KinesisClientLibConfiguration and has a corresponding "with{variableName}" setter method, will be read in, and its
 * value in properties file will be assigned to corresponding variable in KinesisClientLibConfiguration.
 */
public class KinesisClientLibConfigurator {

    private static final Log LOG = LogFactory.getLog(KinesisClientLibConfigurator.class);
    private static final String PREFIX = "with";

    // Required properties
    private static final String PROP_APP_NAME = "applicationName";
    private static final String PROP_STREAM_NAME = "streamName";
    private static final String PROP_CREDENTIALS_PROVIDER_KINESIS = "AWSCredentialsProvider";
    private static final String PROP_CREDENTIALS_PROVIDER_DYNAMODB = "AWSCredentialsProviderDynamoDB";
    private static final String PROP_CREDENTIALS_PROVIDER_CLOUDWATCH = "AWSCredentialsProviderCloudWatch";
    private static final String PROP_WORKER_ID = "workerId";

    private Map, IPropertyValueDecoder> classToDecoder;
    private Map> nameToMethods;

    /**
     * Constructor.
     */
    public KinesisClientLibConfigurator() {
        List> getters =
                Arrays.asList(new IntegerPropertyValueDecoder(),
                        new LongPropertyValueDecoder(),
                        new BooleanPropertyValueDecoder(),
                        new DatePropertyValueDecoder(),
                        new AWSCredentialsProviderPropertyValueDecoder(),
                        new StringPropertyValueDecoder(),
                        new InitialPositionInStreamPropertyValueDecoder(),
                        new ClientConfigurationPropertyValueDecoder(),
                        new SetPropertyValueDecoder());

        classToDecoder = new Hashtable, IPropertyValueDecoder>();
        for (IPropertyValueDecoder getter : getters) {
            for (Class clazz : getter.getSupportedTypes()) {
                /*
                 * We could validate that we never overwrite a getter but we can also do this by manual inspection of
                 * the getters.
                 */
                classToDecoder.put(clazz, getter);
            }
        }
        nameToMethods = new Hashtable>();
        for (Method method : KinesisClientLibConfiguration.class.getMethods()) {
            if (!nameToMethods.containsKey(method.getName())) {
                nameToMethods.put(method.getName(), new ArrayList());
            }
            nameToMethods.get(method.getName()).add(method);
        }
    }

    /**
     * Return a KinesisClientLibConfiguration with variables configured as specified by the properties in config stream.
     * Program will fail immediately, if customer provide: 1) invalid variable value. Program will log it as warning and
     * continue, if customer provide: 1) variable with unsupported variable type. 2) a variable with name which does not
     * match any of the variables in KinesisClientLibConfigration.
     * 
     * @param properties a Properties object containing the configuration information
     * @return KinesisClientLibConfiguration
     */
    public KinesisClientLibConfiguration getConfiguration(Properties properties) {
        // The three minimum required arguments for constructor are obtained first. They are all mandatory, all of them
        // should be provided. If any of these three failed to be set, program will fail.
        IPropertyValueDecoder stringValueDecoder = new StringPropertyValueDecoder();
        IPropertyValueDecoder awsCPPropGetter =
                new AWSCredentialsProviderPropertyValueDecoder();
        String applicationName = stringValueDecoder.decodeValue(properties.getProperty(PROP_APP_NAME));
        String streamName = stringValueDecoder.decodeValue(properties.getProperty(PROP_STREAM_NAME));
        AWSCredentialsProvider provider =
                awsCPPropGetter.decodeValue(properties.getProperty(PROP_CREDENTIALS_PROVIDER_KINESIS));

        if (applicationName == null || applicationName.isEmpty()) {
            throw new IllegalArgumentException("Value of applicationName should be explicitly provided.");
        }
        if (streamName == null || streamName.isEmpty()) {
            throw new IllegalArgumentException("Value of streamName should be explicitly provided.");
        }

        // Decode the DynamoDB credentials provider if it exists.  If not use the Kinesis credentials provider.
        AWSCredentialsProvider providerDynamoDB;
        String propCredentialsProviderDynamoDBValue = properties.getProperty(PROP_CREDENTIALS_PROVIDER_DYNAMODB);
        if (propCredentialsProviderDynamoDBValue == null) {
            providerDynamoDB = provider;
        } else {
            providerDynamoDB = awsCPPropGetter.decodeValue(propCredentialsProviderDynamoDBValue);
        }

        // Decode the CloudWatch credentials provider if it exists.  If not use the Kinesis credentials provider.
        AWSCredentialsProvider providerCloudWatch;
        String propCredentialsProviderCloudWatchValue = properties.getProperty(PROP_CREDENTIALS_PROVIDER_CLOUDWATCH);
        if (propCredentialsProviderCloudWatchValue == null) {
            providerCloudWatch = provider;
        } else {
            providerCloudWatch = awsCPPropGetter.decodeValue(propCredentialsProviderCloudWatchValue);
        }

        // Allow customer not to provide workerId or to provide empty worker id.
        String workerId = stringValueDecoder.decodeValue(properties.getProperty(PROP_WORKER_ID));
        if (workerId == null || workerId.isEmpty()) {
            workerId = UUID.randomUUID().toString();
            LOG.info("Value of workerId is not provided in the properties. WorkerId is automatically "
                    + "assigned as: " + workerId);
        }

        KinesisClientLibConfiguration config =
                new KinesisClientLibConfiguration(applicationName, streamName, provider, providerDynamoDB, providerCloudWatch, workerId);

        Set requiredNames =
                new HashSet(Arrays.asList(PROP_STREAM_NAME,
                        PROP_APP_NAME,
                        PROP_WORKER_ID,
                        PROP_CREDENTIALS_PROVIDER_KINESIS));

        // Set all the variables that are not used for constructor.
        for (Object keyObject : properties.keySet()) {
            String key = keyObject.toString();
            if (!requiredNames.contains(key)) {
                withProperty(key, properties, config);
            }
        }

        return config;
    }

    /**
     * @param configStream the input stream containing the configuration information
     * @return KinesisClientLibConfiguration
     */
    public KinesisClientLibConfiguration getConfiguration(InputStream configStream) {
        Properties properties = new Properties();
        try {
            properties.load(configStream);
        } catch (IOException e) {
            String msg = "Could not load properties from the stream provided";
            throw new IllegalStateException(msg, e);
        } finally {
            try {
                configStream.close();
            } catch (IOException e) {
                String msg = "Encountered error while trying to close properties file.";
                throw new IllegalStateException(msg, e);
            }
        }
        return getConfiguration(properties);
    }

    private void withProperty(String propertyKey, Properties properties, KinesisClientLibConfiguration config) {
        if (propertyKey.isEmpty()) {
            throw new IllegalArgumentException("The property can't be empty string");
        }
        // Assume that all the setters in KinesisClientLibConfiguration are in the following format
        // They all start with "with" followed by the variable name with first letter capitalized
        String targetMethodName = PREFIX + Character.toUpperCase(propertyKey.charAt(0)) + propertyKey.substring(1);
        String propertyValue = properties.getProperty(propertyKey);
        if (nameToMethods.containsKey(targetMethodName)) {
            for (Method method : nameToMethods.get(targetMethodName)) {
                if (method.getParameterTypes().length == 1 && method.getName().equals(targetMethodName)) {
                    Class paramType = method.getParameterTypes()[0];
                    if (classToDecoder.containsKey(paramType)) {
                        IPropertyValueDecoder decoder = classToDecoder.get(paramType);
                        try {
                            method.invoke(config, decoder.decodeValue(propertyValue));
                            LOG.info(String.format("Successfully set property %s with value %s",
                                    propertyKey,
                                    propertyValue));
                            return;
                        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                            // At this point, we really thought that we could call this method.
                            LOG.warn(String.format("Encountered an error while invoking method %s with value %s. "
                                    + "Exception was %s",
                                    method,
                                    propertyValue,
                                    e));
                        } catch (UnsupportedOperationException e) {
                            LOG.warn(String.format("The property %s is not supported as type %s at this time.",
                                    propertyKey,
                                    paramType));
                        }
                    } else {
                        LOG.debug(String.format("No method for decoding parameters of type %s so method %s could not "
                                + "be invoked.",
                                paramType,
                                method));
                    }
                } else {
                    LOG.debug(String.format("Method %s doesn't look like it is appropriate for setting property %s. "
                            + "Looking for something called %s.",
                            method,
                            propertyKey,
                            targetMethodName));
                }
            }
        } else {
            LOG.debug(String.format("There was no appropriately named method for setting property %s.", propertyKey));
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy