org.aludratest.service.gui.web.selenium.TAFMSSeleniumResourceService.java Source code

Java tutorial

Introduction

Here is the source code for org.aludratest.service.gui.web.selenium.TAFMSSeleniumResourceService.java

Source

/*
 * Copyright (C) 2010-2014 Hamburg Sud and the contributors.
 *
 * 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 org.aludratest.service.gui.web.selenium;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.aludratest.config.AludraTestConfig;
import org.aludratest.config.ConfigProperties;
import org.aludratest.config.ConfigProperty;
import org.aludratest.config.Configurable;
import org.aludratest.config.MutablePreferences;
import org.aludratest.config.Preferences;
import org.aludratest.exception.AutomationException;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.NoConnectionReuseStrategy;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.util.EntityUtils;
import org.codehaus.plexus.component.annotations.Requirement;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/** Uses a TAFMS server to obtain Selenium resources. The TAFMS server deals with priorities among different users.
 * 
 * @author falbrech */
@ConfigProperties({
        @ConfigProperty(name = "tafms.url", type = String.class, description = "The base URL to the TAFMS server providing the resources", defaultValue = "http://127.0.0.1:8080/tafms"),
        @ConfigProperty(name = "tafms.user", type = String.class, description = "The user name to use for the TAFMS server", required = true),
        @ConfigProperty(name = "tafms.password", type = String.class, description = "The password to use for the TAFMS server", required = true),
        @ConfigProperty(name = "tafms.jobName", type = String.class, description = "A name to send to TAFMS as identifying job name. This is used for logging and statistics.", required = false),
        @ConfigProperty(name = "tafms.niceLevel", type = int.class, description = "The nice level to use for this job (-20 to 19). This only affects the priority in relation to other running jobs by the same TAFMS user. The lower the value, the higher the priority.", defaultValue = "0") })
public class TAFMSSeleniumResourceService implements SeleniumResourceService, Configurable {

    private static final Logger LOG = LoggerFactory.getLogger(TAFMSSeleniumResourceService.class);

    @Requirement
    private AludraTestConfig aludraConfig;

    private Preferences configuration;

    private Map<String, String> hostResourceIds = new ConcurrentHashMap<String, String>();

    @Override
    public String acquire() {
        // prepare a JSON query to the given TAFMS server
        JSONObject query = new JSONObject();

        try {
            query.put("resourceType", "selenium");
            query.put("niceLevel", configuration.getIntValue("tafms.niceLevel", 0));
            String jobName = configuration.getStringValue("tafms.jobName");
            if (jobName != null && !"".equals(jobName)) {
                query.put("jobName", jobName);
            }
        } catch (JSONException e) {
        }

        // prepare authentication
        BasicCredentialsProvider provider = new BasicCredentialsProvider();
        provider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(
                configuration.getStringValue("tafms.user"), configuration.getStringValue("tafms.password")));

        CloseableHttpClient client = HttpClientBuilder.create()
                .setConnectionReuseStrategy(new NoConnectionReuseStrategy()).disableConnectionState()
                .disableAutomaticRetries().setDefaultCredentialsProvider(provider).build();

        String message = null;
        try {
            boolean wait;

            // use preemptive authentication to avoid double connection count
            AuthCache authCache = new BasicAuthCache();
            // Generate BASIC scheme object and add it to the local auth cache
            BasicScheme basicAuth = new BasicScheme();
            URL url = new URL(getTafmsUrl());
            HttpHost host = new HttpHost(url.getHost(), url.getPort() == -1 ? url.getDefaultPort() : url.getPort(),
                    url.getProtocol());
            authCache.put(host, basicAuth);

            // Add AuthCache to the execution context
            BasicHttpContext localcontext = new BasicHttpContext();
            localcontext.setAttribute(HttpClientContext.AUTH_CACHE, authCache);

            do {
                // send a POST request to resource URL
                HttpPost request = new HttpPost(getTafmsUrl() + "resource");

                // attach query as JSON string data
                request.setEntity(new StringEntity(query.toString(), ContentType.APPLICATION_JSON));

                CloseableHttpResponse response = null;

                // fire request
                response = client.execute(request, localcontext);

                try {
                    if (response.getStatusLine() == null) {
                        throw new ClientProtocolException("No HTTP status line transmitted");
                    }

                    message = extractMessage(response);
                    if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                        LOG.error("Exception when querying TAFMS server for resource. HTTP Status: "
                                + response.getStatusLine().getStatusCode() + ", message: " + message);
                        return null;
                    }

                    JSONObject object = new JSONObject(message);
                    if (object.has("errorMessage")) {
                        LOG.error("TAFMS server reported an error: " + object.get("errorMessage"));
                        return null;
                    }

                    // continue wait?
                    if (object.has("waiting") && object.getBoolean("waiting")) {
                        wait = true;
                        query.put("requestId", object.getString("requestId"));
                    } else {
                        JSONObject resource = object.optJSONObject("resource");
                        if (resource == null) {
                            LOG.error("TAFMS server response did not provide a resource. Message was: " + message);
                            return null;
                        }

                        String sUrl = resource.getString("url");
                        hostResourceIds.put(sUrl, object.getString("requestId"));

                        return sUrl;
                    }
                } finally {
                    IOUtils.closeQuietly(response);
                }
            } while (wait);

            // should never come here
            return null;
        } catch (ClientProtocolException e) {
            LOG.error("Exception in HTTP transmission", e);
            return null;
        } catch (IOException e) {
            LOG.error("Exception in communication with TAFMS server", e);
            return null;
        } catch (JSONException e) {
            LOG.error("Invalid JSON received from TAFMS server. JSON message was: " + message, e);
            return null;
        } finally {
            IOUtils.closeQuietly(client);
        }
    }

    @Override
    public void release(String server) {
        if (server == null) {
            return;
        }

        String resourceKey = hostResourceIds.remove(server);
        if (resourceKey == null) {
            return;
        }

        CloseableHttpClient client = HttpClientBuilder.create()
                .setConnectionReuseStrategy(new NoConnectionReuseStrategy()).disableConnectionState()
                .disableAutomaticRetries().build();

        // send a DELETE request to resource URL
        HttpDelete request = new HttpDelete(getTafmsUrl() + "resource/" + resourceKey);
        CloseableHttpResponse response = null;

        try {
            response = client.execute(request);
        } catch (IOException e) {
            LOG.warn("Could not release TAFMS resource", e);
        } finally {
            IOUtils.closeQuietly(response);
            IOUtils.closeQuietly(client);
        }
    }

    @Override
    public int getHostCount() {
        // Just return number of configured Threads, as resource is "shared" and number can vary
        return aludraConfig.getNumberOfThreads();
    }

    @Override
    public String getPropertiesBaseName() {
        return "tafms";
    }

    @Override
    public void fillDefaults(MutablePreferences preferences) {
    }

    @Override
    public void configure(Preferences preferences) {
        int niceLevel = preferences.getIntValue("tafms.niceLevel", 0);
        if (niceLevel < -20 || niceLevel > 19) {
            throw new AutomationException("Illegal value for tafms.niceLevel: " + niceLevel
                    + ". Value must be from -20 to +19, inclusive");
        }

        String url = preferences.getStringValue("tafms.url");
        try {
            new URL(url);
        } catch (Exception e) {
            throw new AutomationException("Illegal URL for tafms.url: " + url, e);
        }

        String user = preferences.getStringValue("tafms.user");
        if (user == null || "".equals(user)) {
            throw new AutomationException("TAFMS user name is missing");
        }
        String password = preferences.getStringValue("tafms.password");
        if (password == null || "".equals(password)) {
            throw new AutomationException("TAFMS password is missing");
        }

        configuration = preferences;
    }

    private String getTafmsUrl() {
        String url = configuration.getStringValue("tafms.url");
        if (!url.endsWith("/")) {
            url += "/";
        }
        return url;
    }

    private String extractMessage(HttpResponse response) throws IOException {
        if (response.getEntity() == null) {
            return null;
        }

        HttpEntity entity = response.getEntity();
        InputStream in = entity.getContent();

        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            IOUtils.copy(in, baos);
            return new String(baos.toByteArray(), "UTF-8");
        } finally {
            EntityUtils.consumeQuietly(entity);
            IOUtils.closeQuietly(in);
        }
    }

}