/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 hudson.plugins.selenium;
import com.thoughtworks.selenium.grid.hub.HubRegistry;
import com.thoughtworks.selenium.grid.hub.remotecontrol.DynamicRemoteControlPool;
import com.thoughtworks.selenium.grid.hub.remotecontrol.RemoteControlProxy;
import hudson.FilePath;
import hudson.Plugin;
import hudson.model.Action;
import hudson.model.Api;
import hudson.model.Hudson;
import hudson.model.TaskListener;
import hudson.remoting.Callable;
import hudson.remoting.Channel;
import hudson.slaves.Channels;
import hudson.util.ClasspathBuilder;
import hudson.util.StreamTaskListener;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
import org.kohsuke.stapler.framework.io.LargeText;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.logging.Level;
/**
* Starts Selenium Grid server in another JVM.
*
* @author Kohsuke Kawaguchi
*/
@ExportedBean
public class PluginImpl extends Plugin implements Action, Serializable {
private int port = 4444;
private String exclusionPatterns;
private boolean rcBrowserSideLog;
private boolean rcDebug;
private boolean rcTrustAllSSLCerts;
private String rcFirefoxProfileTemplate;
private String rcLog;
private String hubLogLevel = "INFO";
/**
* Channel to Selenium Grid JVM.
*/
private transient Channel channel;
private transient Future<?> hubLauncher;
public void start() throws Exception {
load();
StreamTaskListener listener = new StreamTaskListener(getLogFile());
File root = Hudson.getInstance().getRootDir();
channel = createSeleniumGridVM(root, listener);
Level logLevel = hubLogLevel != null ? Level.parse(hubLogLevel) : Level.INFO;
hubLauncher = channel.callAsync(new HubLauncher(port, logLevel));
Hudson.getInstance().getActions().add(this);
}
public File getLogFile() {
return new File(Hudson.getInstance().getRootDir(),"selenium.log");
}
@Override
public void configure(StaplerRequest req, JSONObject formData) {
port = formData.getInt("port");
exclusionPatterns = formData.getString("exclusionPatterns");
rcLog = formData.getString("rcLog");
rcDebug = formData.getBoolean("rcDebug");
rcBrowserSideLog = formData.getBoolean("rcBrowserSideLog");
rcTrustAllSSLCerts = formData.getBoolean("rcTrustAllSSLCerts");
rcFirefoxProfileTemplate = formData.getString("rcFirefoxProfileTemplate");
hubLogLevel = formData.getString("hubLogLevel");
try {
save();
} catch (IOException e) {
e.printStackTrace();
}
}
public void waitForHubLaunch() throws ExecutionException, InterruptedException {
hubLauncher.get();
}
public String getIconFileName() {
return "/plugin/selenium/24x24/selenium.png";
}
public String getDisplayName() {
return "Selenium Grid";
}
public String getUrlName() {
return "/selenium";
}
public Api getApi() {
return new Api(this);
}
@Exported
public int getPort() {
return port;
}
@Exported
public String getExclusionPatterns(){
return exclusionPatterns;
}
@Exported
public String getRcLog(){
return rcLog;
}
@Exported
public boolean getRcBrowserSideLog(){
return rcBrowserSideLog;
}
@Exported
public boolean getRcDebug(){
return rcDebug;
}
@Exported
public boolean getRcTrustAllSSLCerts() {
return rcTrustAllSSLCerts;
}
@Exported
public String getRcFirefoxProfileTemplate(){
return rcFirefoxProfileTemplate;
}
@Exported
public String getHubLogLevel(){
return hubLogLevel != null ? hubLogLevel : "INFO";
}
public void stop() throws Exception {
channel.close();
}
@Exported(inline=true)
public List<SeleniumRemoteControl> getRemoteControls() throws IOException, InterruptedException {
if(channel==null) return Collections.emptyList();
return channel.call(new Callable<List<SeleniumRemoteControl>,RuntimeException>() {
public List<SeleniumRemoteControl> call() throws RuntimeException {
HubRegistry registry = HubRegistry.registry();
DynamicRemoteControlPool pool = registry.remoteControlPool();
List<SeleniumRemoteControl> r = new ArrayList<SeleniumRemoteControl>();
for (RemoteControlProxy rc : pool.availableRemoteControls())
r.add(new SeleniumRemoteControl(rc,false));
for (RemoteControlProxy rc : pool.reservedRemoteControls())
r.add(new SeleniumRemoteControl(rc,true));
return r;
}
});
}
/**
* Launches Hub in a separate JVM.
*
* @param rootDir
* The slave/master root.
*/
static /*package*/ Channel createSeleniumGridVM(File rootDir, TaskListener listener) throws IOException, InterruptedException {
FilePath distDir = install(rootDir, listener);
return Channels.newJVM("Selenium Grid",listener,distDir,
new ClasspathBuilder().addAll(distDir,"lib/selenium-grid-hub-standalone-*.jar, lib/log4j-*.jar"),
null);
}
/**
* Launches RC in a separate JVM.
*
* @param rootDir
* The slave/master root.
*/
static /*package*/ Channel createSeleniumRCVM(File rootDir, TaskListener listener) throws IOException, InterruptedException {
FilePath distDir = install(rootDir, listener);
return Channels.newJVM("Selenium RC",listener,distDir,
new ClasspathBuilder()
.addAll(distDir,"vendor/selenium-server-*.jar, lib/selenium-grid-remote-control-*.jar, lib/commons-httpclient-*.jar"),
null);
}
private static FilePath install(File rootDir, TaskListener listener) throws IOException, InterruptedException {
FilePath distDir = new FilePath(new File(rootDir,"selenium-grid"));
distDir.installIfNecessaryFrom(PluginImpl.class.getResource("selenium-grid.tgz"),listener,"Installing Selenium Grid binaries");
return distDir;
}
/**
* Determines the host name of the Hudson master.
*/
static /*package*/ String getMasterHostName() throws MalformedURLException {
String rootUrl = Hudson.getInstance().getRootUrl();
if(rootUrl==null)
return null;
URL url = new URL(rootUrl);
return url.getHost();
}
/**
* Handles incremental log.
*/
public void doProgressiveLog( StaplerRequest req, StaplerResponse rsp) throws IOException {
new LargeText(getLogFile(),false).doProgressText(req,rsp);
}
private static final long serialVersionUID = 1L;
}