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

com.unboundid.scim.tools.SCIMQueryRate Maven / Gradle / Ivy

/*
 * Copyright 2011-2019 Ping Identity Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License (GPLv2 only)
 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see .
 */

package com.unboundid.scim.tools;

import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.scim.data.BaseResource;
import com.unboundid.scim.schema.ResourceDescriptor;
import com.unboundid.scim.sdk.Debug;
import com.unboundid.scim.sdk.ResourceNotFoundException;
import com.unboundid.scim.sdk.SCIMEndpoint;
import com.unboundid.scim.sdk.SCIMException;
import com.unboundid.scim.sdk.SCIMService;
import com.unboundid.util.ColumnFormatter;
import com.unboundid.util.CommandLineTool;
import com.unboundid.util.FixedRateBarrier;
import com.unboundid.util.FormattableColumn;
import com.unboundid.util.HorizontalAlignment;
import com.unboundid.util.OutputFormat;
import com.unboundid.util.ValuePattern;
import com.unboundid.util.WakeableSleeper;
import com.unboundid.util.args.ArgumentException;
import com.unboundid.util.args.ArgumentParser;
import com.unboundid.util.args.BooleanArgument;
import com.unboundid.util.args.FileArgument;
import com.unboundid.util.args.IntegerArgument;
import com.unboundid.util.args.StringArgument;
import com.unboundid.util.ssl.KeyStoreKeyManager;
import com.unboundid.util.ssl.PromptTrustManager;
import com.unboundid.util.ssl.SSLUtil;
import com.unboundid.util.ssl.TrustAllTrustManager;
import com.unboundid.util.ssl.TrustStoreTrustManager;

import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.glassfish.jersey.apache.connector.ApacheClientProperties;
import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
import org.glassfish.jersey.client.ClientConfig;

import javax.net.ssl.KeyManager;
import javax.net.ssl.TrustManager;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
import javax.ws.rs.core.MediaType;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.GeneralSecurityException;
import java.text.ParseException;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.ConsoleHandler;

import static com.unboundid.scim.sdk.Debug.debugException;
import static com.unboundid.util.StaticUtils.NO_STRINGS;
import static com.unboundid.scim.tools.ToolMessages.*;
import static com.unboundid.util.StaticUtils.getExceptionMessage;

/**
 * This class provides a tool that can be used to query a SCIM server repeatedly
 * using multiple threads.  It can help provide an estimate of the query
 * performance that a SCIM server is able to achieve.  The query filter may be
 * a value pattern as described in the {@link com.unboundid.util.ValuePattern}
 * class.  This makes it possible to query over a range of resources rather
 * than repeatedly performing queries with the same filter.
 * 

* All of the necessary information is provided using command line arguments. * Supported arguments are as follows: *
    *
  • "-h {address}" or "--hostname {address}" -- Specifies the address of * the SCIM server. If this isn't specified, then a default of * "localhost" will be used.
  • *
  • "-p {port}" or "--port {port}" -- Specifies the port number of the * SCIM server. If this isn't specified, then a default port of 80 * will be used.
  • *
  • "--contextPath {path}" -- specifies the context path of * the SCIM server. If no context path is specified, then the default * value '/' is used.
  • *
  • "--authID {userName}" -- Specifies the authentication ID to use when * authenticating using basic auth.
  • *
  • "-w {password}" or "--authPassword {password}" -- Specifies the * password to use when authenticating using basic auth or a * password-based SASL mechanism.
  • *
  • "--bearerToken {b64token}" -- Specifies the OAuth2 bearer * token to use when authenticating using OAuth
  • *
  • "--resourceName {resource-name}" -- specifies the name of resources to * be queried. If this isn't specified, then a default of "User" will * be used.
  • *
  • "-x" or "--xml" -- Specifies XML format in requests rather than * JSON format.
  • *
  • "-f {filter}" or "--filter {filter}" -- specifies the filter to use for * the queries. It may be a simple filter, or it may be a value pattern * to express a range of filters. If this isn't specified, then no * filtering is requested.
  • *
  • "-A {name}" or "--attribute {name}" -- specifies the name of an * attribute that should be included in resources returned from the * server. If this isn't specified, then all resource attributes will be * requested. Multiple attributes may be requested with multiple instances * of this argument.
  • *
  • "-t {num}" or "--numThreads {num}" -- specifies the number of * concurrent threads to use when performing the queries. If this is not * provided, then a default of one thread will be used.
  • *
  • "-i {sec}" or "--intervalDuration {sec}" -- specifies the length of * time in seconds between lines out output. If this is not provided, * then a default interval duration of five seconds will be used.
  • *
  • "-I {num}" or "--numIntervals {num}" -- specifies the maximum number of * intervals for which to run. If this is not provided, then it will * run forever.
  • *
  • "-r {queries-per-second}" or "--ratePerSecond {queries-per-second}" * -- specifies the target number of queries to perform per second. It * is still necessary to specify a sufficient number of threads for * achieving this rate. If this option is not provided, then the tool * will run at the maximum rate for the specified number of threads.
  • *
  • "--warmUpIntervals {num}" -- specifies the number of intervals to * complete before beginning overall statistics collection.
  • *
  • "--timestampFormat {format}" -- specifies the format to use for * timestamps included before each output line. The format may be one of * "none" (for no timestamps), "with-date" (to include both the date and * the time), or "without-date" (to include only time time).
  • *
  • "-c" or "--csv" -- Generate output in CSV format rather than a * display-friendly format.
  • *
*/ public class SCIMQueryRate extends CommandLineTool { // Arguments used to communicate with a SCIM server. private FileArgument authPasswordFile; private IntegerArgument port; private StringArgument authID; private StringArgument authPassword; private StringArgument bearerToken; private StringArgument contextPath; private StringArgument host; private BooleanArgument trustAll; private BooleanArgument useSSL; private FileArgument keyStorePasswordFile; private FileArgument trustStorePasswordFile; private StringArgument certificateNickname; private StringArgument keyStoreFormat; private StringArgument keyStorePath; private StringArgument keyStorePassword; private StringArgument trustStoreFormat; private StringArgument trustStorePath; private StringArgument trustStorePassword; // The argument used to indicate whether to generate output in CSV format. private BooleanArgument csvFormat; // The argument used to indicate whether to use XML format in requests rather // than JSON format. private BooleanArgument xmlFormat; // The argument used to specify the collection interval. private IntegerArgument collectionInterval; // The argument used to specify the number of intervals. private IntegerArgument numIntervals; // The argument used to specify the number of threads. private IntegerArgument numThreads; // The argument used to specify the seed to use for the random number // generator. private IntegerArgument randomSeed; // The target rate of searches per second. private IntegerArgument ratePerSecond; // The number of warm-up intervals to perform. private IntegerArgument warmUpIntervals; // The argument used to specify the attributes to return. private StringArgument attributes; // The argument used to specify the filters for the queries. private StringArgument filter; // The argument used to specify a resource ID (or pattern). private StringArgument resourceId; // The argument used to specify the name of resources to be queried. private StringArgument resourceName; // The argument used to specify the timestamp format. private StringArgument timestampFormat; // The prompt trust manager that will be shared by all connections created // for which it is appropriate. This will allow them to benefit from the // common cache. private final AtomicReference promptTrustManager = new AtomicReference(); /** * Parse the provided command line arguments and make the appropriate set of * changes. * * @param args The command line arguments provided to this program. */ public static void main(final String[] args) { final ResultCode resultCode = main(args, System.out, System.err); if (resultCode != ResultCode.SUCCESS) { System.exit(resultCode.intValue()); } } /** * Parse the provided command line arguments and make the appropriate set of * changes. * * @param args The command line arguments provided to this program. * @param outStream The output stream to which standard out should be * written. It may be {@code null} if output should be * suppressed. * @param errStream The output stream to which standard error should be * written. It may be {@code null} if error messages * should be suppressed. * * @return A result code indicating whether the processing was successful. */ public static ResultCode main(final String[] args, final OutputStream outStream, final OutputStream errStream) { final SCIMQueryRate queryRate = new SCIMQueryRate(outStream, errStream); return queryRate.runTool(args); } /** * Creates a new instance of this tool. * * @param outStream The output stream to which standard out should be * written. It may be {@code null} if output should be * suppressed. * @param errStream The output stream to which standard error should be * written. It may be {@code null} if error messages * should be suppressed. */ public SCIMQueryRate(final OutputStream outStream, final OutputStream errStream) { super(outStream, errStream); } /** * Retrieves the name for this tool. * * @return The name for this tool. */ @Override() public String getToolName() { return "scim-query-rate"; } /** * Retrieves the description for this tool. * * @return The description for this tool. */ @Override() public String getToolDescription() { return INFO_QUERY_TOOL_DESC.get(); } /** * {@inheritDoc} */ @Override() public void addToolArguments(final ArgumentParser parser) throws ArgumentException { host = new StringArgument( 'h', "hostname", true, 1, INFO_QUERY_TOOL_ARG_PLACEHOLDER_HOSTNAME.get(), INFO_QUERY_TOOL_ARG_DESC_HOSTNAME.get(), "localhost"); parser.addArgument(host); port = new IntegerArgument( 'p', "port", true, 1, INFO_QUERY_TOOL_ARG_PLACEHOLDER_PORT.get(), INFO_QUERY_TOOL_ARG_DESC_PORT.get(), 1, 65535, 80); parser.addArgument(port); contextPath = new StringArgument(null, "contextPath", false, 1, INFO_QUERY_TOOL_ARG_PLACEHOLDER_CONTEXT_PATH.get(), INFO_QUERY_TOOL_ARG_DESC_CONTEXT_PATH.get(), Arrays.asList("/")); parser.addArgument(contextPath); authID = new StringArgument( null, "authID", false, 1, INFO_QUERY_TOOL_ARG_PLACEHOLDER_AUTHID.get(), INFO_QUERY_TOOL_ARG_DESC_AUTHID.get()); parser.addArgument(authID); authPassword = new StringArgument( 'w', "authPassword", false, 1, INFO_QUERY_TOOL_ARG_PLACEHOLDER_AUTH_PASSWORD.get(), INFO_QUERY_TOOL_ARG_DESC_AUTH_PASSWORD.get()); parser.addArgument(authPassword); bearerToken = new StringArgument( null, "bearerToken", false, 1, INFO_QUERY_TOOL_ARG_PLACEHOLDER_BEARER_TOKEN.get(), INFO_QUERY_TOOL_ARG_DESC_BEARER_TOKEN.get()); parser.addArgument(bearerToken); authPasswordFile = new FileArgument( 'j', "authPasswordFile", false, 1, INFO_QUERY_TOOL_ARG_PLACEHOLDER_AUTH_PASSWORD_FILE.get(), INFO_QUERY_TOOL_ARG_DESC_AUTH_PASSWORD_FILE.get(), true, true, true, false); parser.addArgument(authPasswordFile); resourceName = new StringArgument( null, "resourceName", false, 1, INFO_QUERY_TOOL_ARG_PLACEHOLDER_RESOURCE_NAME.get(), INFO_QUERY_TOOL_ARG_DESC_RESOURCE_NAME.get(), null, Arrays.asList("User")); parser.addArgument(resourceName); xmlFormat = new BooleanArgument( 'x', "xml", 1, INFO_QUERY_TOOL_ARG_DESC_XML_FORMAT.get()); parser.addArgument(xmlFormat); filter = new StringArgument( 'f', "filter", false, 1, INFO_QUERY_TOOL_ARG_PLACEHOLDER_FILTER.get(), INFO_QUERY_TOOL_ARG_DESC_FILTER.get()); parser.addArgument(filter); resourceId = new StringArgument( 'd', "resourceID", false, 1, INFO_QUERY_TOOL_ARG_PLACEHOLDER_RESOURCE_ID.get(), INFO_QUERY_TOOL_ARG_DESC_RESOURCE_ID.get()); parser.addArgument(resourceId); attributes = new StringArgument( 'A', "attribute", false, 0, INFO_QUERY_TOOL_ARG_PLACEHOLDER_ATTRIBUTE.get(), INFO_QUERY_TOOL_ARG_DESC_ATTRIBUTE.get()); parser.addArgument(attributes); numThreads = new IntegerArgument( 't', "numThreads", true, 1, INFO_QUERY_TOOL_ARG_PLACEHOLDER_NUM_THREADS.get(), INFO_QUERY_TOOL_ARG_DESC_NUM_THREADS.get(), 1, Integer.MAX_VALUE, 1); parser.addArgument(numThreads); collectionInterval = new IntegerArgument( 'i', "intervalDuration", true, 1, INFO_QUERY_TOOL_ARG_PLACEHOLDER_INTERVAL_DURATION.get(), INFO_QUERY_TOOL_ARG_DESC_INTERVAL_DURATION.get(), 1, Integer.MAX_VALUE, 5); parser.addArgument(collectionInterval); numIntervals = new IntegerArgument( 'I', "numIntervals", true, 1, INFO_QUERY_TOOL_ARG_PLACEHOLDER_NUM_INTERVALS.get(), INFO_QUERY_TOOL_ARG_DESC_NUM_INTERVALS.get(), 1, Integer.MAX_VALUE, Integer.MAX_VALUE); parser.addArgument(numIntervals); ratePerSecond = new IntegerArgument( 'r', "ratePerSecond", false, 1, INFO_QUERY_TOOL_ARG_PLACEHOLDER_RATE_PER_SECOND.get(), INFO_QUERY_TOOL_ARG_DESC_RATE_PER_SECOND.get(), 1, Integer.MAX_VALUE); parser.addArgument(ratePerSecond); warmUpIntervals = new IntegerArgument( null, "warmUpIntervals", true, 1, INFO_QUERY_TOOL_ARG_PLACEHOLDER_WARM_UP_INTERVALS.get(), INFO_QUERY_TOOL_ARG_DESC_WARM_UP_INTERVALS.get(), 0, Integer.MAX_VALUE, 0); parser.addArgument(warmUpIntervals); final LinkedHashSet allowedFormats = new LinkedHashSet(3); allowedFormats.add("none"); allowedFormats.add("with-date"); allowedFormats.add("without-date"); timestampFormat = new StringArgument( null, "timestampFormat", true, 1, INFO_QUERY_TOOL_ARG_PLACEHOLDER_TIMESTAMP_FORMAT.get(), INFO_QUERY_TOOL_ARG_DESC_TIMESTAMP_FORMAT.get(), allowedFormats, "none"); parser.addArgument(timestampFormat); csvFormat = new BooleanArgument( 'c', "csv", 1, INFO_QUERY_TOOL_ARG_DESC_CSV_FORMAT.get()); parser.addArgument(csvFormat); randomSeed = new IntegerArgument( 'R', "randomSeed", false, 1, INFO_QUERY_TOOL_ARG_PLACEHOLDER_RANDOM_SEED.get(), INFO_QUERY_TOOL_ARG_DESC_RANDOM_SEED.get()); parser.addArgument(randomSeed); useSSL = new BooleanArgument('Z', "useSSL", 1, INFO_SCIM_TOOL_DESCRIPTION_USE_SSL.get()); parser.addArgument(useSSL); trustAll = new BooleanArgument('X', "trustAll", 1, INFO_SCIM_TOOL_DESCRIPTION_TRUST_ALL.get()); parser.addArgument(trustAll); keyStorePath = new StringArgument('K', "keyStorePath", false, 1, INFO_SCIM_TOOL_PLACEHOLDER_PATH.get(), INFO_SCIM_TOOL_DESCRIPTION_KEY_STORE_PATH.get()); parser.addArgument(keyStorePath); keyStorePassword = new StringArgument('W', "keyStorePassword", false, 1, INFO_SCIM_TOOL_PLACEHOLDER_PASSWORD.get(), INFO_SCIM_TOOL_DESCRIPTION_KEY_STORE_PASSWORD.get()); parser.addArgument(keyStorePassword); keyStorePasswordFile = new FileArgument('u', "keyStorePasswordFile", false, 1, INFO_SCIM_TOOL_PLACEHOLDER_PATH.get(), INFO_SCIM_TOOL_DESCRIPTION_KEY_STORE_PASSWORD_FILE.get()); parser.addArgument(keyStorePasswordFile); keyStoreFormat = new StringArgument(null, "keyStoreFormat", false, 1, INFO_SCIM_TOOL_PLACEHOLDER_FORMAT.get(), INFO_SCIM_TOOL_DESCRIPTION_KEY_STORE_FORMAT.get()); parser.addArgument(keyStoreFormat); trustStorePath = new StringArgument('P', "trustStorePath", false, 1, INFO_SCIM_TOOL_PLACEHOLDER_PATH.get(), INFO_SCIM_TOOL_DESCRIPTION_TRUST_STORE_PATH.get()); parser.addArgument(trustStorePath); trustStorePassword = new StringArgument('T', "trustStorePassword", false, 1, INFO_SCIM_TOOL_PLACEHOLDER_PASSWORD.get(), INFO_SCIM_TOOL_DESCRIPTION_TRUST_STORE_PASSWORD.get()); parser.addArgument(trustStorePassword); trustStorePasswordFile = new FileArgument('U', "trustStorePasswordFile", false, 1, INFO_SCIM_TOOL_PLACEHOLDER_PATH.get(), INFO_SCIM_TOOL_DESCRIPTION_TRUST_STORE_PASSWORD_FILE.get()); parser.addArgument(trustStorePasswordFile); trustStoreFormat = new StringArgument(null, "trustStoreFormat", false, 1, INFO_SCIM_TOOL_PLACEHOLDER_FORMAT.get(), INFO_SCIM_TOOL_DESCRIPTION_TRUST_STORE_FORMAT.get()); parser.addArgument(trustStoreFormat); certificateNickname = new StringArgument('N', "certNickname", false, 1, INFO_SCIM_TOOL_PLACEHOLDER_CERT_NICKNAME.get(), INFO_SCIM_TOOL_DESCRIPTION_CERT_NICKNAME.get()); parser.addArgument(certificateNickname); parser.addDependentArgumentSet(authID, authPassword, authPasswordFile); parser.addExclusiveArgumentSet(authPassword, authPasswordFile, bearerToken); parser.addExclusiveArgumentSet(authID, bearerToken); parser.addExclusiveArgumentSet(keyStorePassword, keyStorePasswordFile); parser.addExclusiveArgumentSet(trustStorePassword, trustStorePasswordFile); parser.addExclusiveArgumentSet(trustAll, trustStorePath); parser.addExclusiveArgumentSet(filter, resourceId); } /** * {@inheritDoc} */ @Override() public LinkedHashMap getExampleUsages() { final LinkedHashMap examples = new LinkedHashMap(); final String[] args1 = { "--hostname", "server.example.com", "--port", "80", "--authID", "admin", "--authPassword", "password", "--xml", "--filter", "userName eq \"user.[1-1000000]\"", "--attribute", "userName", "--attribute", "name", "--numThreads", "8" }; examples.put(args1, INFO_QUERY_TOOL_EXAMPLE_1.get()); final String[] args2 = { "--hostname", "server.example.com", "--port", "80", "--authID", "admin", "--authPassword", "password", "--resourceID", "uid=user.[1-1000000],ou=people,dc=example,dc=com", "--attribute", "userName", "--attribute", "name", "--numThreads", "8" }; examples.put(args2, INFO_QUERY_TOOL_EXAMPLE_2.get()); return examples; } /** * Performs the actual processing for this tool. In this case, it gets a * connection to the directory server and uses it to perform the requested * searches. * * @return The result code for the processing that was performed. */ @Override() public ResultCode doToolProcessing() { //Initalize the Debugger Debug.setEnabled(true); Debug.getLogger().addHandler(new ConsoleHandler()); Debug.getLogger().setUseParentHandlers(false); // Determine the random seed to use. final Long seed; if (randomSeed.isPresent()) { seed = Long.valueOf(randomSeed.getValue()); } else { seed = null; } // Create a value pattern for the filter. final ValuePattern filterPattern; boolean isQuery = true; if (filter.isPresent()) { try { filterPattern = new ValuePattern(filter.getValue(), seed); } catch (ParseException pe) { Debug.debugException(pe); err(ERR_QUERY_TOOL_BAD_FILTER_PATTERN.get(pe.getMessage())); return ResultCode.PARAM_ERROR; } } else if (resourceId.isPresent()) { isQuery = false; try { filterPattern = new ValuePattern(resourceId.getValue()); } catch (ParseException pe) { Debug.debugException(pe); err(ERR_QUERY_TOOL_BAD_RESOURCE_ID_PATTERN.get(pe.getMessage())); return ResultCode.PARAM_ERROR; } } else { filterPattern = null; } // Get the attributes to return. final String[] attrs; if (attributes.isPresent()) { final List attrList = attributes.getValues(); attrs = new String[attrList.size()]; attrList.toArray(attrs); } else { attrs = NO_STRINGS; } // If the --ratePerSecond option was specified, then limit the rate // accordingly. FixedRateBarrier fixedRateBarrier = null; if (ratePerSecond.isPresent()) { final int intervalSeconds = collectionInterval.getValue(); final int ratePerInterval = ratePerSecond.getValue() * intervalSeconds; fixedRateBarrier = new FixedRateBarrier(1000L * intervalSeconds, ratePerInterval); } // Determine whether to include timestamps in the output and if so what // format should be used for them. final boolean includeTimestamp; final String timeFormat; if (timestampFormat.getValue().equalsIgnoreCase("with-date")) { includeTimestamp = true; timeFormat = "dd/MM/yyyy HH:mm:ss"; } else if (timestampFormat.getValue().equalsIgnoreCase("without-date")) { includeTimestamp = true; timeFormat = "HH:mm:ss"; } else { includeTimestamp = false; timeFormat = null; } // Determine whether any warm-up intervals should be run. final long totalIntervals; final boolean warmUp; int remainingWarmUpIntervals = warmUpIntervals.getValue(); if (remainingWarmUpIntervals > 0) { warmUp = true; totalIntervals = 0L + numIntervals.getValue() + remainingWarmUpIntervals; } else { warmUp = true; totalIntervals = 0L + numIntervals.getValue(); } // Create the table that will be used to format the output. final OutputFormat outputFormat; if (csvFormat.isPresent()) { outputFormat = OutputFormat.CSV; } else { outputFormat = OutputFormat.COLUMNS; } final ColumnFormatter formatter = new ColumnFormatter(includeTimestamp, timeFormat, outputFormat, " ", new FormattableColumn(15, HorizontalAlignment.RIGHT, "Recent", "Queries/Sec"), new FormattableColumn(15, HorizontalAlignment.RIGHT, "Recent", "Avg Dur ms"), new FormattableColumn(15, HorizontalAlignment.RIGHT, "Recent", "Resources/Query"), new FormattableColumn(15, HorizontalAlignment.RIGHT, "Recent", "Errors/Sec"), new FormattableColumn(15, HorizontalAlignment.RIGHT, "Overall", "Queries/Sec"), new FormattableColumn(15, HorizontalAlignment.RIGHT, "Overall", "Avg Dur ms")); // Create values to use for statistics collection. final AtomicLong queryCounter = new AtomicLong(0L); final AtomicLong resourceCounter = new AtomicLong(0L); final AtomicLong errorCounter = new AtomicLong(0L); final AtomicLong queryDurations = new AtomicLong(0L); // Determine the length of each interval in milliseconds. final long intervalMillis = 1000L * collectionInterval.getValue(); // We will use Apache's HttpClient library for this tool. SSLUtil sslUtil; try { sslUtil = createSSLUtil(); } catch (LDAPException e) { debugException(e); err(e.getMessage()); return e.getResultCode(); } RegistryBuilder registryBuilder = RegistryBuilder.create(); final String schemeName; if (sslUtil != null) { try { SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslUtil.createSSLContext("TLS"), new NoopHostnameVerifier()); schemeName = "https"; registryBuilder.register(schemeName, sslConnectionSocketFactory); } catch (GeneralSecurityException e) { debugException(e); err(ERR_SCIM_TOOL_CANNOT_CREATE_SSL_CONTEXT.get( getExceptionMessage(e))); return ResultCode.LOCAL_ERROR; } } else { schemeName = "http"; registryBuilder.register(schemeName, new PlainConnectionSocketFactory()); } final Registry socketFactoryRegistry = registryBuilder.build(); RequestConfig requestConfig = RequestConfig.custom() .setConnectionRequestTimeout(30000) .setExpectContinueEnabled(true).build(); SocketConfig socketConfig = SocketConfig.custom() .setSoTimeout(30000) .setSoReuseAddress(true) .build(); final PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager(socketFactoryRegistry); mgr.setMaxTotal(numThreads.getValue()); mgr.setDefaultMaxPerRoute(numThreads.getValue()); mgr.setDefaultSocketConfig(socketConfig); mgr.setValidateAfterInactivity(-1); ClientConfig jerseyConfig = new ClientConfig(); jerseyConfig.property(ApacheClientProperties.CONNECTION_MANAGER, mgr); jerseyConfig.property(ApacheClientProperties.REQUEST_CONFIG, requestConfig); ApacheConnectorProvider connectorProvider = new ApacheConnectorProvider(); jerseyConfig.connectorProvider(connectorProvider); if (authID.isPresent()) { try { final String password; if (authPassword.isPresent()) { password = authPassword.getValue(); } else if (authPasswordFile.isPresent()) { password = authPasswordFile.getNonBlankFileLines().get(0); } else { password = null; } BasicCredentialsProvider provider = new BasicCredentialsProvider(); provider.setCredentials( new AuthScope(host.getValue(), port.getValue()), new UsernamePasswordCredentials(authID.getValue(), password) ); jerseyConfig.property( ApacheClientProperties.CREDENTIALS_PROVIDER, provider); jerseyConfig.property( ApacheClientProperties.PREEMPTIVE_BASIC_AUTHENTICATION, true); } catch (IOException e) { Debug.debugException(e); err(ERR_QUERY_TOOL_SET_BASIC_AUTH.get(e.getMessage())); return ResultCode.LOCAL_ERROR; } } else if (bearerToken.isPresent()) { jerseyConfig.register( new ClientRequestFilter() { public void filter(final ClientRequestContext clientRequestContext) throws IOException { try { clientRequestContext.getHeaders().add( "Authorization", "Bearer " + bearerToken.getValue()); } catch (Exception ex) { throw new RuntimeException( "Unable to add authorization handler", ex); } } } ); } // Create the SCIM client to use for the queries. final URI uri; try { final String path; if (contextPath.getValue().startsWith("/")) { path = contextPath.getValue(); } else { path = "/" + contextPath.getValue(); } uri = new URI(schemeName, null, host.getValue(), port.getValue(), path, null, null); } catch (URISyntaxException e) { Debug.debugException(e); err(ERR_QUERY_TOOL_CANNOT_CREATE_URL.get(e.getMessage())); return ResultCode.OTHER; } final SCIMService service = new SCIMService(uri, jerseyConfig); if (xmlFormat.isPresent()) { service.setContentType(MediaType.APPLICATION_XML_TYPE); service.setAcceptType(MediaType.APPLICATION_XML_TYPE); } // Retrieve the resource schema. final ResourceDescriptor resourceDescriptor; try { resourceDescriptor = service.getResourceDescriptor(resourceName.getValue(), null); if(resourceDescriptor == null) { throw new ResourceNotFoundException("Resource " + resourceName.getValue() + " is not defined by the service provider"); } } catch (SCIMException e) { Debug.debugException(e); err(ERR_QUERY_TOOL_RETRIEVE_RESOURCE_SCHEMA.get(e.getMessage())); return ResultCode.OTHER; } final SCIMEndpoint endpoint = service.getEndpoint(resourceDescriptor, BaseResource.BASE_RESOURCE_FACTORY); // Create the threads to use for the searches. final CyclicBarrier barrier = new CyclicBarrier(numThreads.getValue() + 1); final QueryRateThread[] threads = new QueryRateThread[numThreads.getValue()]; for (int i=0; i < threads.length; i++) { threads[i] = new QueryRateThread(i, isQuery, endpoint, filterPattern, attrs, barrier, queryCounter, resourceCounter, queryDurations, errorCounter, fixedRateBarrier); threads[i].start(); } // Display the table header. for (final String headerLine : formatter.getHeaderLines(true)) { out(headerLine); } // Indicate that the threads can start running. try { barrier.await(); } catch (Exception e) { Debug.debugException(e); } long overallStartTime = System.nanoTime(); long nextIntervalStartTime = System.currentTimeMillis() + intervalMillis; boolean setOverallStartTime = false; long lastDuration = 0L; long lastNumEntries = 0L; long lastNumErrors = 0L; long lastNumSearches = 0L; long lastEndTime = System.nanoTime(); for (long i=0; i < totalIntervals; i++) { final long startTimeMillis = System.currentTimeMillis(); final long sleepTimeMillis = nextIntervalStartTime - startTimeMillis; nextIntervalStartTime += intervalMillis; try { if (sleepTimeMillis > 0) { Thread.sleep(sleepTimeMillis); } } catch (Exception e) { Debug.debugException(e); } final long endTime = System.nanoTime(); final long intervalDuration = endTime - lastEndTime; final long numSearches; final long numEntries; final long numErrors; final long totalDuration; if (warmUp && (remainingWarmUpIntervals > 0)) { numSearches = queryCounter.getAndSet(0L); numEntries = resourceCounter.getAndSet(0L); numErrors = errorCounter.getAndSet(0L); totalDuration = queryDurations.getAndSet(0L); } else { numSearches = queryCounter.get(); numEntries = resourceCounter.get(); numErrors = errorCounter.get(); totalDuration = queryDurations.get(); } final long recentNumSearches = numSearches - lastNumSearches; final long recentNumEntries = numEntries - lastNumEntries; final long recentNumErrors = numErrors - lastNumErrors; final long recentDuration = totalDuration - lastDuration; final double numSeconds = intervalDuration / 1000000000.0d; final double recentSearchRate = recentNumSearches / numSeconds; final double recentErrorRate = recentNumErrors / numSeconds; final double recentAvgDuration; final double recentEntriesPerSearch; if (recentNumSearches > 0L) { recentEntriesPerSearch = 1.0d * recentNumEntries / recentNumSearches; recentAvgDuration = 1.0d * recentDuration / recentNumSearches / 1000000; } else { recentEntriesPerSearch = 0.0d; recentAvgDuration = 0.0d; } if (warmUp && (remainingWarmUpIntervals > 0)) { out(formatter.formatRow(recentSearchRate, recentAvgDuration, recentEntriesPerSearch, recentErrorRate, "warming up", "warming up")); remainingWarmUpIntervals--; if (remainingWarmUpIntervals == 0) { out(INFO_QUERY_TOOL_WARM_UP_COMPLETED.get()); setOverallStartTime = true; } } else { if (setOverallStartTime) { overallStartTime = lastEndTime; setOverallStartTime = false; } final double numOverallSeconds = (endTime - overallStartTime) / 1000000000.0d; final double overallSearchRate = numSearches / numOverallSeconds; final double overallAvgDuration; if (numSearches > 0L) { overallAvgDuration = 1.0d * totalDuration / numSearches / 1000000; } else { overallAvgDuration = 0.0d; } out(formatter.formatRow(recentSearchRate, recentAvgDuration, recentEntriesPerSearch, recentErrorRate, overallSearchRate, overallAvgDuration)); lastNumSearches = numSearches; lastNumEntries = numEntries; lastNumErrors = numErrors; lastDuration = totalDuration; } lastEndTime = endTime; } // Stop all of the threads. ResultCode resultCode = ResultCode.SUCCESS; for (final QueryRateThread t : threads) { t.signalShutdown(); } // Interrupt any blocked threads after a grace period. final WakeableSleeper sleeper = new WakeableSleeper(); sleeper.sleep(1000); mgr.shutdown(); for (final QueryRateThread t : threads) { final ResultCode r = t.waitForShutdown(); if (resultCode == ResultCode.SUCCESS) { resultCode = r; } } return resultCode; } /** * Creates the SSLUtil instance to use for secure communication. * * @return The SSLUtil instance to use for secure communication, or * {@code null} if secure communication is not needed. * * @throws LDAPException If a problem occurs while creating the SSLUtil * instance. */ private SSLUtil createSSLUtil() throws LDAPException { if (useSSL.isPresent()) { KeyManager keyManager = null; if (keyStorePath.isPresent()) { char[] pw = null; if (keyStorePassword.isPresent()) { pw = keyStorePassword.getValue().toCharArray(); } else if (keyStorePasswordFile.isPresent()) { try { pw = keyStorePasswordFile.getNonBlankFileLines().get(0). toCharArray(); } catch (Exception e) { Debug.debugException(e); throw new LDAPException(ResultCode.LOCAL_ERROR, ERR_SCIM_TOOL_CANNOT_READ_KEY_STORE_PASSWORD.get( getExceptionMessage(e)), e); } } try { keyManager = new KeyStoreKeyManager(keyStorePath.getValue(), pw, keyStoreFormat.getValue(), certificateNickname.getValue()); } catch (Exception e) { Debug.debugException(e); throw new LDAPException(ResultCode.LOCAL_ERROR, ERR_SCIM_TOOL_CANNOT_CREATE_KEY_MANAGER.get( getExceptionMessage(e)), e); } } TrustManager trustManager; if (trustAll.isPresent()) { trustManager = new TrustAllTrustManager(false); } else if (trustStorePath.isPresent()) { char[] pw = null; if (trustStorePassword.isPresent()) { pw = trustStorePassword.getValue().toCharArray(); } else if (trustStorePasswordFile.isPresent()) { try { pw = trustStorePasswordFile.getNonBlankFileLines().get(0). toCharArray(); } catch (Exception e) { Debug.debugException(e); throw new LDAPException(ResultCode.LOCAL_ERROR, ERR_SCIM_TOOL_CANNOT_READ_TRUST_STORE_PASSWORD.get( getExceptionMessage(e)), e); } } trustManager = new TrustStoreTrustManager(trustStorePath.getValue(), pw, trustStoreFormat.getValue(), true); } else { trustManager = promptTrustManager.get(); if (trustManager == null) { final PromptTrustManager m = new PromptTrustManager(); promptTrustManager.compareAndSet(null, m); trustManager = promptTrustManager.get(); } } return new SSLUtil(keyManager, trustManager); } else { return null; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy