Skip to content
This repository has been archived by the owner on Oct 12, 2022. It is now read-only.

Friendly Provider determination #21

Merged
merged 18 commits into from
Apr 6, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.oauth2.useragent;

import java.util.List;
import java.util.Map;

public interface ProviderScanner {

/**
* Scans the {@link Provider} implementations, checks their requirements
* and tries to find one that will work, preferring the one named by the
* {@code userAgentProvider} property, if it was set.
*
* @return a {@link Provider} that was deemed compatible;
* {@code null} if no compatible providers were found.
*/
Provider findCompatibleProvider();

/**
* Scans the {@link Provider} implementations, checks their requirements
* and tries to find one that will work, preferring the one whose
* name was provided.
*
* @param userAgentProvider the name of the preferred provider or
* {@code null} if there's no preference
* @return a {@link Provider} that was deemed compatible;
* {@code null} if no compatible providers were found.
*/
Provider findCompatibleProvider(final String userAgentProvider);

/**
* Determines unmet requirements (if any) for each available {@link Provider} implementation.
*
* @return a map of {@link Provider} instances to their list of unmet requirements.
* If a {@link Provider} had all its requirements met, it will not be part of the map.
* An empty map means all providers are compatible.
*/
Map<Provider, List<String>> getUnmetProviderRequirements();

/**
* Calls {@link #findCompatibleProvider()} and
* returns whether a compatible {@link Provider} was found.
*
* @return {@code true} if a compatible provider was found;
* {@code false} otherwise.
*/
boolean hasCompatibleProvider();

/**
* Calls {@link #findCompatibleProvider(String)} and
* returns whether a compatible {@link Provider} was found.
*
* @param userAgentProvider the name of the preferred provider or
* {@code null} if there's no preference
* @return {@code true} if a compatible provider was found;
* {@code false} otherwise.
*/
boolean hasCompatibleProvider(final String userAgentProvider);

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.microsoft.alm.oauth2.useragent.subprocess.TestableProcess;
import com.microsoft.alm.oauth2.useragent.subprocess.TestableProcessFactory;
import com.microsoft.alm.oauth2.useragent.utils.PackageLocator;
import com.microsoft.alm.oauth2.useragent.utils.StringHelper;

import java.io.BufferedReader;
import java.io.File;
Expand All @@ -25,19 +26,21 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

public class UserAgentImpl implements UserAgent {
public class UserAgentImpl implements UserAgent, ProviderScanner {

static final String REQUEST_AUTHORIZATION_CODE = "requestAuthorizationCode";
static final String JAVA_VERSION_STRING = System.getProperty("java.version");
static final String JAVA_HOME = System.getProperty("java.home");
static final String PATH_SEPARATOR = System.getProperty("path.separator");
static final String NEW_LINE = System.getProperty("line.separator");
static final String UTF_8 = "UTF-8";
static final String USER_AGENT_PROVIDER_PROPERTY_NAME = "userAgentProvider";

private static final String[] EMPTY_STRING_ARRAY = new String[0];
private static final Set<String> NETWORKING_PROPERTY_NAMES;
Expand Down Expand Up @@ -81,17 +84,66 @@ public class UserAgentImpl implements UserAgent {
}

private final TestableProcessFactory processFactory;
private final List<Provider> candidateProviders;
private final LinkedHashMap<Provider, List<String>> requirementsByProvider = new LinkedHashMap<Provider, List<String>>();
private Provider provider;
private boolean hasScannedAtLeastOnce = false;
private String userAgentProvider = null;

public UserAgentImpl() {
this(new DefaultProcessFactory(), null);
this(new DefaultProcessFactory(), null, Provider.PROVIDERS);
}

UserAgentImpl(final TestableProcessFactory processFactory, final Provider provider) {
UserAgentImpl(final TestableProcessFactory processFactory, final Provider provider, final List<Provider> candidateProviders) {
this.processFactory = processFactory;
this.candidateProviders = candidateProviders;
this.provider = provider;
}

@Override
public Provider findCompatibleProvider() {
final String userAgentProvider = System.getProperty(USER_AGENT_PROVIDER_PROPERTY_NAME);
return findCompatibleProvider(userAgentProvider, true);
}

@Override
public Provider findCompatibleProvider(final String userAgentProvider) {
return findCompatibleProvider(userAgentProvider, true);
}

Provider findCompatibleProvider(final String userAgentProvider, final boolean checkOverrideIsCompatible) {
if (provider == null || !StringHelper.equal(this.userAgentProvider, userAgentProvider)) {
requirementsByProvider.clear();
provider = scanProviders(userAgentProvider, candidateProviders, requirementsByProvider, checkOverrideIsCompatible);
hasScannedAtLeastOnce = true;
this.userAgentProvider = userAgentProvider;
}
return provider;
}

@Override
public Map<Provider, List<String>> getUnmetProviderRequirements() {
if (!hasScannedAtLeastOnce) {
findCompatibleProvider();
}

final Map<Provider, List<String>> copy = new LinkedHashMap<Provider, List<String>>(requirementsByProvider);
final Map<Provider, List<String>> result = Collections.unmodifiableMap(copy);
return result;
}

@Override
public boolean hasCompatibleProvider() {
findCompatibleProvider();
return provider != null;
}

@Override
public boolean hasCompatibleProvider(final String userAgentProvider) {
findCompatibleProvider(userAgentProvider);
return provider != null;
}

@Override
public AuthorizationResponse requestAuthorizationCode(final URI authorizationEndpoint, final URI redirectUri)
throws AuthorizationException {
Expand All @@ -104,9 +156,10 @@ AuthorizationResponse encode(final String methodName, final String... parameters
final ArrayList<String> classPath = new ArrayList<String>();
// TODO: should we append ".exe" on Windows?
command.add(new File(JAVA_HOME, "bin/java").getAbsolutePath());
final String userAgentProvider = System.getProperty(USER_AGENT_PROVIDER_PROPERTY_NAME);
findCompatibleProvider(userAgentProvider, false);
if (provider == null) {
final String userAgentProvider = System.getProperty("userAgentProvider");
provider = determineProvider(userAgentProvider);
throwUnsupported(requirementsByProvider);
}
provider.augmentProcessParameters(command, classPath);

Expand Down Expand Up @@ -157,70 +210,77 @@ void addClassPathToCommand(final List<String> classPath, final List<String> comm
command.add("-classpath");
//noinspection ToArrayCallWithZeroLengthArrayArgument
final String[] classPathComponents = classPath.toArray(EMPTY_STRING_ARRAY);
final String classPathString = join(pathSeparator, classPathComponents);
final String classPathString = StringHelper.join(pathSeparator, classPathComponents);
command.add(classPathString);
}

static String join(final String separator, final String[] value)
{
if (value == null)
throw new IllegalArgumentException("value is null");

// "If separator is null, an empty string (String.Empty) is used instead."
final String sep = separator == null ? "" : separator;
static void throwUnsupported(final Map<Provider, List<String>> unmetRequirements) {
final StringBuilder sb = new StringBuilder("I don't support your platform yet.");
describeUnmetRequirements(unmetRequirements, sb);
sb.append(NEW_LINE);
sb.append("Please send details about your operating system version, Java version, 32- vs. 64-bit, etc.");
sb.append(NEW_LINE);
sb.append("The following System Properties and Environment Variables would be very useful.");
sb.append(NEW_LINE);

final StringBuilder result = new StringBuilder();
final Properties properties = System.getProperties();
appendProperties(properties, sb);
sb.append(NEW_LINE);

if (value.length > 0) {
result.append(value[0] == null ? "" : value[0]);
for (int i = 1; i < value.length; i++) {
result.append(sep);
result.append(value[i] == null ? "" : value[i]);
}
}
final Map<String, String> variables = System.getenv();
appendVariables(variables, sb);

return result.toString();
throw new IllegalStateException(sb.toString());
}

static Provider determineProvider(final String userAgentProvider) {
return determineProvider(userAgentProvider, Provider.PROVIDERS);
}
static Provider scanProviders(final String userAgentProvider, final List<Provider> providers, final Map<Provider, List<String>> destinationUnmetRequirements, final boolean checkOverrideIsCompatible) {

static Provider determineProvider(final String userAgentProvider, final List<Provider> providers) {
Provider result = null;

if (userAgentProvider != null) {
for (final Provider provider : providers) {
if (provider.getClassName().equals(userAgentProvider)) {
return provider;
if (checkOverrideIsCompatible) {
final List<String> requirements = provider.checkRequirements();
if (requirements == null || requirements.size() == 0) {
result = provider;
}
}
else {
result = provider;
}
break;
}
}
}
final StringBuilder sb = new StringBuilder("I don't support your platform yet.");

for (final Provider provider : providers) {
final List<String> requirements = provider.checkRequirements();
if (requirements == null || requirements.size() == 0) {
return provider;
if (result == null) {
result = provider;
}
}
sb.append(NEW_LINE);
sb.append("Unmet requirements for the '").append(provider.getClassName()).append("' provider:").append(NEW_LINE);
for (final String requirement : requirements) {
sb.append(" - ").append(requirement).append(NEW_LINE);
else {
destinationUnmetRequirements.put(provider, requirements);
}
}
sb.append(NEW_LINE);
sb.append("Please send details about your operating system version, Java version, 32- vs. 64-bit, etc.");
sb.append(NEW_LINE);
sb.append("The following System Properties and Environment Variables would be very useful.");
sb.append(NEW_LINE);

final Properties properties = System.getProperties();
appendProperties(properties, sb);
sb.append(NEW_LINE);

final Map<String, String> variables = System.getenv();
appendVariables(variables, sb);
return result;
}

throw new IllegalStateException(sb.toString());
static void describeUnmetRequirements(final Map<Provider, List<String>> unmetRequirements, final StringBuilder destination) {
for (final Map.Entry<Provider, List<String>> pair: unmetRequirements.entrySet()) {
final Provider provider = pair.getKey();
final List<String> requirements = pair.getValue();
if (requirements != null && requirements.size() > 0) {
destination.append(NEW_LINE);
destination.append("Unmet requirements for the '").append(provider.getClassName()).append("' provider:").append(NEW_LINE);
for (final String requirement : requirements) {
destination.append(" - ").append(requirement).append(NEW_LINE);
}
}
}
}

static void appendProperties(final Properties properties, final StringBuilder destination) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.oauth2.useragent.utils;

public class StringHelper {

public static boolean equal(final String s1, final String s2) {
if (s1 == null) {
return s2 == null;
}
return s1.equals(s2);
}

public static String join(final String separator, final String[] value) {
if (value == null)
throw new IllegalArgumentException("value is null");

// "If separator is null, an empty string (String.Empty) is used instead."
final String sep = separator == null ? "" : separator;

final StringBuilder result = new StringBuilder();

if (value.length > 0) {
result.append(value[0] == null ? "" : value[0]);
for (int i = 1; i < value.length; i++) {
result.append(sep);
result.append(value[i] == null ? "" : value[i]);
}
}

return result.toString();
}
}
Loading