Java tutorial
/* * Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. * Portions copyright Titan: Distributed Graph Database - Copyright 2012 and onwards Aurelius. * * Licensed under the Apache License, Version 2.0 (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/apache2.0 * * 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.amazon.janusgraph.diskstorage.dynamodb; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import lombok.AccessLevel; import lombok.Getter; import lombok.NonNull; import org.janusgraph.diskstorage.configuration.Configuration; import org.janusgraph.graphdb.configuration.GraphDatabaseConfiguration; import com.amazonaws.ClientConfiguration; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.AWSCredentialsProvider; import com.amazonaws.auth.AWSStaticCredentialsProvider; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.util.concurrent.RateLimiter; import com.google.common.util.concurrent.RateLimiterCreator; /** * Operations setting up the DynamoDB client. * * @author Matthew Sowders * @author Alexander Patrikalakis * */ public class Client { private static final String VALIDATE_CREDENTIALS_CLASS_NAME = "Must provide either an AWSCredentials or AWSCredentialsProvider fully qualified class name"; private static final double DEFAULT_BURST_BUCKET_SIZE_IN_SECONDS = 300.0; private final Map<String, Long> capacityRead = new HashMap<>(); private final Map<String, Long> capacityWrite = new HashMap<>(); private final Map<String, BackendDataModel> dataModelMap = new HashMap<>(); @Getter(AccessLevel.PACKAGE) private final boolean forceConsistentRead; @Getter(AccessLevel.PACKAGE) private final boolean enableParallelScan; private final Map<String, Integer> scanLimitMap = new HashMap<>(); @Getter private final DynamoDbDelegate delegate; @Getter private final String prefix; public Client(final Configuration config) { final String credentialsClassName = config.get(Constants.DYNAMODB_CREDENTIALS_CLASS_NAME); final Class<?> clazz; try { clazz = Class.forName(credentialsClassName); } catch (ClassNotFoundException e) { throw new IllegalArgumentException(VALIDATE_CREDENTIALS_CLASS_NAME, e); } final String[] credentialsConstructorArgsValues = config .get(Constants.DYNAMODB_CREDENTIALS_CONSTRUCTOR_ARGS); final List<String> filteredArgList = new ArrayList<>(); for (Object obj : credentialsConstructorArgsValues) { final String str = obj.toString(); if (!str.isEmpty()) { filteredArgList.add(str); } } final AWSCredentialsProvider credentialsProvider; if (AWSCredentials.class.isAssignableFrom(clazz)) { final AWSCredentials credentials = createCredentials(clazz, filteredArgList.toArray(new String[filteredArgList.size()])); credentialsProvider = new AWSStaticCredentialsProvider(credentials); } else if (AWSCredentialsProvider.class.isAssignableFrom(clazz)) { credentialsProvider = createCredentialsProvider(clazz, credentialsConstructorArgsValues); } else { throw new IllegalArgumentException(VALIDATE_CREDENTIALS_CLASS_NAME); } //begin adaptation of constructor at //https://github.com/buka/titan/blob/master/src/main/java/com/thinkaurelius/titan/diskstorage/dynamodb/DynamoDBClient.java#L77 final ClientConfiguration clientConfig = new ClientConfiguration(); clientConfig.withConnectionTimeout(config.get(Constants.DYNAMODB_CLIENT_CONN_TIMEOUT)) .withConnectionTTL(config.get(Constants.DYNAMODB_CLIENT_CONN_TTL)) .withMaxConnections(config.get(Constants.DYNAMODB_CLIENT_MAX_CONN)) .withMaxErrorRetry(config.get(Constants.DYNAMODB_CLIENT_MAX_ERROR_RETRY)) .withGzip(config.get(Constants.DYNAMODB_CLIENT_USE_GZIP)) .withReaper(config.get(Constants.DYNAMODB_CLIENT_USE_REAPER)) .withUserAgentSuffix(config.get(Constants.DYNAMODB_CLIENT_USER_AGENT)) .withSocketTimeout(config.get(Constants.DYNAMODB_CLIENT_SOCKET_TIMEOUT)) .withSocketBufferSizeHints(config.get(Constants.DYNAMODB_CLIENT_SOCKET_BUFFER_SEND_HINT), config.get(Constants.DYNAMODB_CLIENT_SOCKET_BUFFER_RECV_HINT)) .withProxyDomain(config.get(Constants.DYNAMODB_CLIENT_PROXY_DOMAIN)) .withProxyWorkstation(config.get(Constants.DYNAMODB_CLIENT_PROXY_WORKSTATION)) .withProxyHost(config.get(Constants.DYNAMODB_CLIENT_PROXY_HOST)) .withProxyPort(config.get(Constants.DYNAMODB_CLIENT_PROXY_PORT)) .withProxyUsername(config.get(Constants.DYNAMODB_CLIENT_PROXY_USERNAME)) .withProxyPassword(config.get(Constants.DYNAMODB_CLIENT_PROXY_PASSWORD)); forceConsistentRead = config.get(Constants.DYNAMODB_FORCE_CONSISTENT_READ); //end adaptation of constructor at //https://github.com/buka/titan/blob/master/src/main/java/com/thinkaurelius/titan/diskstorage/dynamodb/DynamoDBClient.java#L77 enableParallelScan = config.get(Constants.DYNAMODB_ENABLE_PARALLEL_SCAN); prefix = config.get(Constants.DYNAMODB_TABLE_PREFIX); final String metricsPrefix = config.get(Constants.DYNAMODB_METRICS_PREFIX); final long maxRetries = config.get(Constants.DYNAMODB_MAX_SELF_THROTTLED_RETRIES); Preconditions.checkArgument(maxRetries >= 0, Constants.DYNAMODB_MAX_SELF_THROTTLED_RETRIES.getName() + " must be at least 0"); final long retryMillis = config.get(Constants.DYNAMODB_INITIAL_RETRY_MILLIS); Preconditions.checkArgument(retryMillis > 0, Constants.DYNAMODB_INITIAL_RETRY_MILLIS.getName() + " must be at least 1"); final double controlPlaneRate = config.get(Constants.DYNAMODB_CONTROL_PLANE_RATE); Preconditions.checkArgument(controlPlaneRate >= 0, "must have a positive control plane rate"); final RateLimiter controlPlaneRateLimiter = RateLimiter.create(controlPlaneRate); final Map<String, RateLimiter> readRateLimit = new HashMap<>(); final Map<String, RateLimiter> writeRateLimit = new HashMap<>(); final Set<String> storeNames = new HashSet<>(Constants.REQUIRED_BACKEND_STORES); storeNames.add(config.get(GraphDatabaseConfiguration.IDS_STORE_NAME)); storeNames.addAll(config.getContainedNamespaces(Constants.DYNAMODB_STORES_NAMESPACE)); storeNames.forEach(storeName -> setupStore(config, readRateLimit, writeRateLimit, storeName)); delegate = new DynamoDbDelegate( JanusGraphConfigUtil.getNullableConfigValue(config, Constants.DYNAMODB_CLIENT_ENDPOINT), JanusGraphConfigUtil.getNullableConfigValue(config, Constants.DYNAMODB_CLIENT_SIGNING_REGION), credentialsProvider, clientConfig, config, readRateLimit, writeRateLimit, maxRetries, retryMillis, prefix, metricsPrefix, controlPlaneRateLimiter); } private void setupStore(final Configuration config, final Map<String, RateLimiter> readRateLimit, final Map<String, RateLimiter> writeRateLimit, final String store) { final String dataModel = config.get(Constants.STORES_DATA_MODEL, store); final int scanLimit = config.get(Constants.STORES_SCAN_LIMIT, store); final long readCapacity = config.get(Constants.STORES_INITIAL_CAPACITY_READ, store); final long writeCapacity = config.get(Constants.STORES_INITIAL_CAPACITY_WRITE, store); final double readRate = config.get(Constants.STORES_READ_RATE_LIMIT, store); final double writeRate = config.get(Constants.STORES_WRITE_RATE_LIMIT, store); final String actualTableName = prefix + "_" + store; this.dataModelMap.put(store, BackendDataModel.valueOf(dataModel)); this.capacityRead.put(actualTableName, readCapacity); this.capacityWrite.put(actualTableName, writeCapacity); readRateLimit.put(actualTableName, RateLimiterCreator.createBurstingLimiter(readRate, DEFAULT_BURST_BUCKET_SIZE_IN_SECONDS)); writeRateLimit.put(actualTableName, RateLimiterCreator.createBurstingLimiter(writeRate, DEFAULT_BURST_BUCKET_SIZE_IN_SECONDS)); this.scanLimitMap.put(actualTableName, scanLimit); } long readCapacity(@NonNull final String tableName) { return capacityRead.get(tableName); } long writeCapacity(@NonNull final String tableName) { return capacityWrite.get(tableName); } BackendDataModel dataModel(final String storeName) { return dataModelMap.get(storeName); } int scanLimit(final String tableName) { return scanLimitMap.get(tableName); } private static AWSCredentialsProvider createCredentialsProvider(final Class<?> clazz, final String[] credentialsProviderConstructorArgs) { return (AWSCredentialsProvider) createInstance(clazz, credentialsProviderConstructorArgs); } private static AWSCredentials createCredentials(final Class<?> clazz, final String[] credentialsConstructorArgs) { return (AWSCredentials) createInstance(clazz, credentialsConstructorArgs); } private static Object createInstance(final Class<?> clazz, final String[] constructorArgs) { final Class<?>[] constructorTypes; String[] actualArgs = constructorArgs; if (null == constructorArgs) { constructorTypes = new Class<?>[0]; } else if (constructorArgs.length == 1 && Strings.isNullOrEmpty(constructorArgs[0])) { // Special case for empty constructors actualArgs = new String[0]; constructorTypes = new Class<?>[0]; } else { constructorTypes = new Class<?>[constructorArgs.length]; for (int i = 0; i < constructorArgs.length; i++) { constructorTypes[i] = String.class; } } final Constructor<?> constructor; try { constructor = clazz.getConstructor(constructorTypes); } catch (NoSuchMethodException | SecurityException e) { throw new IllegalArgumentException( "Cannot access constructor:" + clazz.getCanonicalName() + "(" + constructorTypes.length + ")", e); } final Object instance; try { instance = constructor.newInstance((Object[]) actualArgs); } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new IllegalArgumentException("Cannot create new instance:" + clazz.getCanonicalName(), e); } return instance; } }