org.openqa.grid.internal.utils.SelfRegisteringRemote.java Source code

Java tutorial

Introduction

Here is the source code for org.openqa.grid.internal.utils.SelfRegisteringRemote.java

Source

/*
 * Copyright 2007-2011 Selenium committers
 * 
 * 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.openqa.grid.internal.utils;

import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicHttpEntityEnclosingRequest;
import org.apache.http.message.BasicHttpRequest;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.openqa.grid.common.RegistrationRequest;
import org.openqa.grid.common.exception.GridConfigurationException;
import org.openqa.grid.common.exception.GridException;
import org.openqa.selenium.remote.internal.HttpClientFactory;
import org.openqa.grid.web.servlet.ResourceServlet;
import org.openqa.grid.web.utils.ExtraServletUtil;
import org.openqa.jetty.http.HttpContext;
import org.openqa.jetty.jetty.Server;
import org.openqa.jetty.jetty.servlet.ServletHandler;
import org.openqa.selenium.Platform;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.server.RemoteControlConfiguration;
import org.openqa.selenium.server.SeleniumServer;
import org.openqa.selenium.remote.server.log.LoggingManager;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.InvalidParameterException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import javax.servlet.Servlet;

import static org.openqa.grid.common.RegistrationRequest.AUTO_REGISTER;

public class SelfRegisteringRemote {

    private static final Logger log = Logger.getLogger(SelfRegisteringRemote.class.getName());

    private RegistrationRequest nodeConfig;

    private final HttpClientFactory httpClientFactory;

    public SelfRegisteringRemote(RegistrationRequest config) {
        this.nodeConfig = config;
        this.httpClientFactory = new HttpClientFactory();
    }

    public URL getRemoteURL() {
        String host = (String) nodeConfig.getConfiguration().get(RegistrationRequest.HOST);
        String port = (String) nodeConfig.getConfiguration().get(RegistrationRequest.PORT);
        String url = "http://" + host + ":" + port;

        try {
            return new URL(url);
        } catch (MalformedURLException e) {
            throw new GridConfigurationException("error building the node url " + e.getMessage(), e);
        }
    }

    private SeleniumServer server;

    public void startRemoteServer() throws Exception {

        System.setProperty("org.openqa.jetty.http.HttpRequest.maxFormContentSize", "0");

        nodeConfig.validate();
        RemoteControlConfiguration remoteControlConfiguration = nodeConfig.getRemoteControlConfiguration();

        try {
            final String CLIENT_TIMEOUT = "timeout";
            final String BROWSER_TIMEOUT = "browserTimeout";
            JSONObject hubParameters = getHubConfiguration(CLIENT_TIMEOUT, BROWSER_TIMEOUT);
            if (hubParameters.has(CLIENT_TIMEOUT)) {
                int timeout = hubParameters.getInt(CLIENT_TIMEOUT) / 1000;
                remoteControlConfiguration.setTimeoutInSeconds(timeout);
            }
            if (hubParameters.has(BROWSER_TIMEOUT)) {
                int browserTimeout = hubParameters.getInt(BROWSER_TIMEOUT);
                remoteControlConfiguration.setBrowserTimeoutInMs(browserTimeout);
            }
        } catch (Exception e) {
            log.warning("error getting the parameters from the hub. The node may end up with wrong timeouts."
                    + e.getMessage());
        }

        server = new SeleniumServer(remoteControlConfiguration);

        Server jetty = server.getServer();

        String servletsStr = (String) nodeConfig.getConfiguration().get(GridNodeConfiguration.SERVLETS);
        if (servletsStr != null) {
            List<String> servlets = Arrays.asList(servletsStr.split(","));

            HttpContext extra = new HttpContext();

            extra.setContextPath("/extra");
            ServletHandler handler = new ServletHandler();
            handler.addServlet("/resources/*", ResourceServlet.class.getName());

            for (String s : servlets) {
                Class<? extends Servlet> servletClass = ExtraServletUtil.createServlet(s);
                if (servletClass != null) {
                    String path = "/" + servletClass.getSimpleName() + "/*";
                    String clazz = servletClass.getCanonicalName();
                    handler.addServlet(path, clazz);
                    log.info("started extra node servlet visible at : http://xxx:"
                            + nodeConfig.getConfiguration().get(RegistrationRequest.PORT) + "/extra" + path);
                }
            }
            extra.addHandler(handler);
            jetty.addContext(extra);
        }

        server.boot();
    }

    public void stopRemoteServer() {
        if (server != null) {
            server.stop();
        }
    }

    public void deleteAllBrowsers() {
        nodeConfig.getCapabilities().clear();
    }

    // TODO freynaud keep specified platform if specified. At least for unit test purpose.
    /**
     * Adding the browser described by the capability, automatically finding out what platform the
     * node is launched from ( and overriding it if it was specified )
     * 
     * @param cap describing the browser
     * @param instances number of times this browser can be started on the node.
     */
    public void addBrowser(DesiredCapabilities cap, int instances) {
        String s = cap.getBrowserName();
        if (s == null || "".equals(s)) {
            throw new InvalidParameterException(cap + " does seems to be a valid browser.");
        }
        cap.setPlatform(Platform.getCurrent());
        cap.setCapability(RegistrationRequest.MAX_INSTANCES, instances);
        nodeConfig.getCapabilities().add(cap);
    }

    /**
     * sends 1 registration request, bypassing the retry logic and the proxy already registered check.
     * Use only for testing.
     */
    public void sendRegistrationRequest() {

        registerToHub(false);
    }

    /**
     * register the hub following the configuration :
     * <p/>
     * - check if the proxy is already registered before sending a reg request.
     * <p/>
     * - register again every X ms is specified in the config of the node.
     */
    public void startRegistrationProcess() {
        log.info("using the json request : " + nodeConfig.toJSON());

        Boolean register = (Boolean) nodeConfig.getConfiguration().get(AUTO_REGISTER);

        if (!register) {
            log.info("no registration sent ( " + AUTO_REGISTER + " = false )");
        } else {
            final int registerCycleInterval = nodeConfig.getConfigAsInt(RegistrationRequest.REGISTER_CYCLE, 0);
            if (registerCycleInterval > 0) {
                new Thread(new Runnable() { // Thread safety reviewed

                    public void run() {
                        boolean first = true;
                        log.info("Starting auto register thread. Will try to register every "
                                + registerCycleInterval + " ms.");
                        while (true) {
                            try {
                                boolean checkForPresence = true;
                                if (first) {
                                    first = false;
                                    checkForPresence = false;
                                }
                                registerToHub(checkForPresence);
                            } catch (GridException e) {
                                log.info("couldn't register this node : " + e.getMessage());
                            }
                            try {
                                Thread.sleep(registerCycleInterval);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            // While we wait for someone to rewrite server logging.
                            LoggingManager.perSessionLogHandler().clearThreadTempLogs();
                        }
                    }
                }).start();
            } else {
                registerToHub(false);
            }
        }
        LoggingManager.perSessionLogHandler().clearThreadTempLogs();
    }

    public void setTimeout(int timeout, int cycle) {
        nodeConfig.getConfiguration().put(RegistrationRequest.TIME_OUT, timeout);
        nodeConfig.getConfiguration().put(RegistrationRequest.CLEAN_UP_CYCLE, cycle);
    }

    public void setMaxConcurrent(int max) {
        nodeConfig.getConfiguration().put(RegistrationRequest.MAX_SESSION, max);
    }

    public Map<String, Object> getConfiguration() {
        return nodeConfig.getConfiguration();
    }

    private void registerToHub(boolean checkPresenceFirst) {
        if (!checkPresenceFirst || !isAlreadyRegistered(nodeConfig)) {
            String tmp = "http://" + nodeConfig.getConfiguration().get(RegistrationRequest.HUB_HOST) + ":"
                    + nodeConfig.getConfiguration().get(RegistrationRequest.HUB_PORT) + "/grid/register";

            HttpClient client = httpClientFactory.getHttpClient();
            try {
                URL registration = new URL(tmp);
                log.info("Registering the node to hub :" + registration);

                BasicHttpEntityEnclosingRequest r = new BasicHttpEntityEnclosingRequest("POST",
                        registration.toExternalForm());
                String json = nodeConfig.toJSON();
                r.setEntity(new StringEntity(json));

                HttpHost host = new HttpHost(registration.getHost(), registration.getPort());
                HttpResponse response = client.execute(host, r);
                if (response.getStatusLine().getStatusCode() != 200) {
                    throw new RuntimeException("Error sending the registration request.");
                }
            } catch (Exception e) {
                throw new GridException("Error sending the registration request.", e);
            }
        } else {
            log.fine("The node is already present on the hub. Skipping registration.");
        }

    }

    /**
     * uses the hub API to get some of its configuration.
     * @param parameters list of the parameter to be retrieved from the hub
     * @return
     * @throws Exception
     */
    private JSONObject getHubConfiguration(String... parameters) throws Exception {
        String hubApi = "http://" + nodeConfig.getConfiguration().get(RegistrationRequest.HUB_HOST) + ":"
                + nodeConfig.getConfiguration().get(RegistrationRequest.HUB_PORT) + "/grid/api/hub";

        HttpClient client = httpClientFactory.getHttpClient();

        URL api = new URL(hubApi);
        HttpHost host = new HttpHost(api.getHost(), api.getPort());
        String url = api.toExternalForm();
        BasicHttpEntityEnclosingRequest r = new BasicHttpEntityEnclosingRequest("GET", url);

        JSONObject j = new JSONObject();
        JSONArray keys = new JSONArray();

        j.put("configuration", keys);
        r.setEntity(new StringEntity(j.toString()));

        HttpResponse response = client.execute(host, r);
        return extractObject(response);
    }

    private boolean isAlreadyRegistered(RegistrationRequest node) {

        HttpClient client = httpClientFactory.getHttpClient();
        try {
            String tmp = "http://" + node.getConfiguration().get(RegistrationRequest.HUB_HOST) + ":"
                    + node.getConfiguration().get(RegistrationRequest.HUB_PORT) + "/grid/api/proxy";
            URL api = new URL(tmp);
            HttpHost host = new HttpHost(api.getHost(), api.getPort());

            String id = (String) node.getConfiguration().get(RegistrationRequest.ID);
            if (id == null) {
                id = (String) node.getConfiguration().get(RegistrationRequest.REMOTE_HOST);
            }
            BasicHttpRequest r = new BasicHttpRequest("GET", api.toExternalForm() + "?id=" + id);

            HttpResponse response = client.execute(host, r);
            if (response.getStatusLine().getStatusCode() != 200) {
                throw new GridException("Hub is down or not responding.");
            }
            JSONObject o = extractObject(response);
            return (Boolean) o.get("success");
        } catch (Exception e) {
            throw new GridException("Hub is down or not responding: " + e.getMessage());
        }
    }

    private static JSONObject extractObject(HttpResponse resp) throws IOException, JSONException {
        BufferedReader rd = new BufferedReader(new InputStreamReader(resp.getEntity().getContent()));
        StringBuilder s = new StringBuilder();
        String line;
        while ((line = rd.readLine()) != null) {
            s.append(line);
        }
        rd.close();
        return new JSONObject(s.toString());
    }

}