com.mgmtp.jfunk.web.CapabilitiesProvider.java Source code

Java tutorial

Introduction

Here is the source code for com.mgmtp.jfunk.web.CapabilitiesProvider.java

Source

/*
 * Copyright (c) 2015 mgm technology partners GmbH
 *
 * 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.mgmtp.jfunk.web;

import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.collect.Lists.newArrayListWithExpectedSize;
import static com.google.common.collect.Lists.transform;
import static com.google.common.collect.Maps.newHashMap;
import static com.google.common.collect.Maps.newHashMapWithExpectedSize;
import static com.google.common.collect.Maps.transformEntries;
import static com.google.common.collect.Maps.transformValues;
import static org.apache.commons.lang3.StringUtils.substringAfter;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.inject.Inject;
import javax.inject.Provider;

import org.openqa.selenium.Capabilities;
import org.openqa.selenium.Proxy;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;

import com.google.common.base.Function;
import com.google.common.collect.ForwardingMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps.EntryTransformer;
import com.mgmtp.jfunk.common.util.Configuration;

/**
 * <p>
 * Creates a {@link Map} of {@link Capabilities} by driver type, i. e. it is possible to configure
 * different capabilities for different driver types ({@code chrome}, {@code firefox},
 * {@code remote}, etc.). Global capabilities are considered for all driver types.
 * Driver-type-specific capabilities override global ones.
 * </p>
 * <p>
 * List capabilities (e. g. {@code chrome.switches}) are supported. They must have a unique integer
 * index(after the capability name, separated by a period) which is used to identity them as list
 * capabilities. The index has otherwise not special meaning.
 * </p>
 * 
 * <b>Configuring global capabilities:</b>
 * 
 * <pre>
 * webdriver.capability.globalCapability = globalCapabilityValue
 * </pre>
 * 
 * <b>Configuring capabilities per driver type:</b>
 * 
 * <pre>
 * webdriver.firefox.capability.firefoxCapability = firefoxCapabilityValue
 * webdriver.firefox.capability.globalCapability = globalCapabilityValueOverride
 * 
 * webdriver.remote.capability.stringCapability = stringCapabilityValue
 * webdriver.remote.capability.chrome.switches.1 = --window-size=1200,1200
 * webdriver.remote.capability.chrome.switches.2 = --window-position=-1210,0
 * </pre>
 * 
 * <b>Configuring a proxy</b>
 * 
 * <pre>
 * webdriver.proxy.httpProxy=localhost:8080
 * </pre>
 * 
 * All properties the class {@link Proxy} supports may be used (prefixed by {@code webdriver.proxy}
 * ). Currently, these are:
 * 
 * <pre>
 * proxyType
 * ftpProxy
 * httpProxy
 * httpsProxy
 * noProxy
 * sslProxy
 * socksProxy
 * socksUsername
 * socksPassword
 * proxyAutoconfigUrl
 * autodetect
 * </pre>
 * 
 * @author rnaegele
 */
public class CapabilitiesProvider implements Provider<Map<String, DesiredCapabilities>> {

    private static final String PROXY_PREFIX = "webdriver.proxy.";

    private static final Pattern CAPABILITIES_PREFIX_PATTERN = Pattern
            .compile("webdriver[.](?:([^.]+)[.])?capability");

    private final Provider<Configuration> configProvider;

    @Inject
    protected CapabilitiesProvider(final Provider<Configuration> configProvider) {
        this.configProvider = configProvider;
    }

    @Override
    public Map<String, DesiredCapabilities> get() {
        Configuration config = configProvider.get();

        Map<String, Map<String, List<JFunkCapability>>> capabilitiesMap = newHashMap();
        for (Entry<String, String> entry : config.entrySet()) {
            String key = entry.getKey();
            Matcher matcher = CAPABILITIES_PREFIX_PATTERN.matcher(key);
            if (matcher.find()) {
                String driverType = matcher.groupCount() == 1 && matcher.group(1) != null ? matcher.group(1)
                        : "global";
                String capabilityString = key.substring(matcher.end() + 1);
                int lastDotIndex = capabilityString.lastIndexOf('.');
                String value = entry.getValue();

                JFunkCapability capability;
                if (lastDotIndex != -1) {
                    JFunkCapabilityType type = JFunkCapabilityType.LIST;
                    try {
                        Integer.parseInt(capabilityString.substring(lastDotIndex + 1));
                        capabilityString = capabilityString.substring(0, lastDotIndex);
                    } catch (NumberFormatException ex) {
                        // not a list capability
                        type = JFunkCapabilityType.STRING;
                    }
                    capability = new JFunkCapability(capabilityString, value, type);
                } else {
                    capability = new JFunkCapability(capabilityString, value, JFunkCapabilityType.STRING);
                }

                Map<String, List<JFunkCapability>> map = capabilitiesMap.get(driverType);
                if (map == null) {
                    map = newHashMapWithExpectedSize(5);
                    capabilitiesMap.put(driverType, map);
                }
                List<JFunkCapability> list = map.get(capability.name);
                if (list == null) {
                    list = newArrayListWithExpectedSize(1);
                    map.put(capability.name, list);
                }
                list.add(capability);
            }
        }

        Map<String, List<JFunkCapability>> tmpGlobals = capabilitiesMap.remove("global");
        final Map<String, Object> globalCapabilities = tmpGlobals == null ? ImmutableMap.<String, Object>of()
                : transformCapabilities(tmpGlobals);

        final Proxy proxy = createProxyFromConfig(config);

        // transform in to map of capabilities for each webdriver type
        final Map<String, DesiredCapabilities> byDriverTypeCapabilities = transformEntries(capabilitiesMap,
                new EntryTransformer<String, Map<String, List<JFunkCapability>>, DesiredCapabilities>() {
                    @Override
                    public DesiredCapabilities transformEntry(final String key,
                            final Map<String, List<JFunkCapability>> value) {
                        Map<String, Object> capabilities = newHashMap(globalCapabilities);
                        Map<String, Object> transformedCapabilities = transformCapabilities(value);
                        capabilities.putAll(transformedCapabilities);

                        DesiredCapabilities result = new DesiredCapabilities(capabilities);
                        if (proxy != null) {
                            result.setCapability(CapabilityType.PROXY, proxy);
                        }
                        return result;
                    }
                });

        // wrap, so we get empty capabilities instead of nulls
        return new ForwardingMap<String, DesiredCapabilities>() {
            @Override
            protected Map<String, DesiredCapabilities> delegate() {
                return byDriverTypeCapabilities;
            }

            @Override
            public DesiredCapabilities get(final Object key) {
                DesiredCapabilities capabilities = super.get(key);
                if (capabilities == null) {
                    DesiredCapabilities desiredCapabilities = new DesiredCapabilities();
                    if (proxy != null) {
                        desiredCapabilities.setCapability(CapabilityType.PROXY, proxy);
                    }
                    capabilities = desiredCapabilities;
                }
                return capabilities;
            }
        };
    }

    private Proxy createProxyFromConfig(final Configuration config) {
        Map<String, String> proxyConfig = newHashMap();

        for (Entry<String, String> entry : config.entrySet()) {
            String key = entry.getKey();
            if (key.startsWith(PROXY_PREFIX)) {
                proxyConfig.put(substringAfter(key, PROXY_PREFIX), entry.getValue());
            }
        }

        if (proxyConfig.isEmpty()) {
            return null;
        }
        return new Proxy(proxyConfig);
    }

    private Map<String, Object> transformCapabilities(final Map<String, List<JFunkCapability>> capabilitiesMap) {
        return transformValues(capabilitiesMap, new Function<List<JFunkCapability>, Object>() {
            @Override
            public Object apply(final List<JFunkCapability> list) {
                if (list.size() == 1) {
                    JFunkCapability capability = getOnlyElement(list);
                    return capability.type == JFunkCapabilityType.LIST ? ImmutableList.of(capability.value)
                            : capability.value;
                }
                return transform(list, new Function<JFunkCapability, String>() {
                    @Override
                    public String apply(final JFunkCapability capability) {
                        return capability.value;
                    }
                });
            }
        });
    }

    private static class JFunkCapability {
        private final String name;
        private final String value;
        private final JFunkCapabilityType type;

        public JFunkCapability(final String name, final String value, final JFunkCapabilityType type) {
            this.name = name;
            this.value = value;
            this.type = type;
        }
    }

    private static enum JFunkCapabilityType {
        STRING, LIST;
    }
}