Java tutorial
/* * Copyright 2014-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Amazon Software License (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/asl/ * * or in the "license" file accompanying this file. This file 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 = "AWSCredentialsProvider"; private static final String PROP_WORKER_ID = "workerId"; private Map<Class<?>, IPropertyValueDecoder<?>> classToDecoder; private Map<String, List<Method>> nameToMethods; /** * Constructor. */ public KinesisClientLibConfigurator() { List<IPropertyValueDecoder<? extends Object>> getters = Arrays.asList(new IntegerPropertyValueDecoder(), new LongPropertyValueDecoder(), new BooleanPropertyValueDecoder(), new AWSCredentialsProviderPropertyValueDecoder(), new StringPropertyValueDecoder(), new InitialPositionInStreamPropertyValueDecoder(), new ClientConfigurationPropertyValueDecoder(), new SetPropertyValueDecoder()); classToDecoder = new Hashtable<Class<?>, 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<String, List<Method>>(); for (Method method : KinesisClientLibConfiguration.class.getMethods()) { if (!nameToMethods.containsKey(method.getName())) { nameToMethods.put(method.getName(), new ArrayList<Method>()); } 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<String> stringValueDecoder = new StringPropertyValueDecoder(); IPropertyValueDecoder<AWSCredentialsProvider> 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)); 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."); } // 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, workerId); Set<String> requiredNames = new HashSet<String>( Arrays.asList(PROP_STREAM_NAME, PROP_APP_NAME, PROP_WORKER_ID, PROP_CREDENTIALS_PROVIDER)); // 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)); } } }