package hudson.plugins.selenium;
import hudson.Extension;
import hudson.FilePath.FileCallable;
import hudson.model.Computer;
import hudson.model.Hudson;
import hudson.model.Label;
import hudson.model.TaskListener;
import hudson.remoting.VirtualChannel;
import hudson.slaves.ComputerListener;
import hudson.util.IOException2;
import org.apache.commons.lang.ArrayUtils;
import org.springframework.util.StringUtils;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* When a new slave is connected, launch a selenium RC.
*
* @author Kohsuke Kawaguchi
*/
@Extension
public class ComputerListenerImpl extends ComputerListener implements Serializable {
/**
* Starts a selenium RC remotely.
*/
public void onOnline(Computer c, final TaskListener listener) throws IOException, InterruptedException {
LOGGER.fine("Examining if we need to start Selenium RC");
PluginImpl p = Hudson.getInstance().getPlugin(PluginImpl.class);
final String exclusions = p.getExclusionPatterns();
List<String> exclusionPatterns = new ArrayList<String>();
if (StringUtils.hasText(exclusions)) {
exclusionPatterns = Arrays.asList(exclusions.split(SEPARATOR));
}
if (exclusionPatterns.size() > 0){
// loop over all the labels and check if we need to exclude a node based on the exlusionPatterns
for(Label l : c.getNode().getAssignedLabels()) {
for(String pattern : exclusionPatterns){
if (l.toString().matches(pattern)) {
LOGGER.fine("Node " + c.getNode().getDisplayName() + " is excluded from Selenium Grid because its label '"
+ l + "' matches exclusion pattern '" + pattern + "'");
return;
}
}
}
}
final String masterName = PluginImpl.getMasterHostName();
if(masterName==null) {
listener.getLogger().println("Unable to determine the host name of the master. Skipping Selenium execution.");
return;
}
final String hostName = c.getHostName();
if(hostName==null) {
listener.getLogger().println("Unable to determine the host name. Skipping Selenium execution.");
return;
}
final int masterPort = p.getPort();
final int nrc = c.getNumExecutors();
final StringBuilder labelList = new StringBuilder();
for(Label l : c.getNode().getAssignedLabels()) {
labelList.append('/');
labelList.append(l);
}
labelList.append('/');
// user defined parameters for starting the RC
final List<String> userArgs = new ArrayList<String>();
if (hasText(p.getRcLog())){
userArgs.add("-log");
userArgs.add(p.getRcLog());
}
if (p.getRcBrowserSideLog()){
userArgs.add("-browserSideLog");
}
if (p.getRcDebug()){
userArgs.add("-debug");
}
if (p.getRcTrustAllSSLCerts()){
userArgs.add("-trustAllSSLCertificates");
}
if (hasText(p.getRcFirefoxProfileTemplate())){
userArgs.add("-firefoxProfileTemplate");
userArgs.add(p.getRcFirefoxProfileTemplate());
}
// make sure that Selenium Hub is started before we start RCs.
try {
p.waitForHubLaunch();
} catch (ExecutionException e) {
throw new IOException2("Failed to wait for the Hub launch to complete",e);
}
LOGGER.fine("Going to start "+nrc+" RCs on "+c.getName());
c.getNode().getRootPath().actAsync(new FileCallable<Object>() {
public Object invoke(File f, VirtualChannel channel) throws IOException {
try {
for (int i=0; i<nrc; i++) {
// this is potentially unsafe way to figure out a free port number, but it's far easier
// than patching Selenium
ServerSocket ss = new ServerSocket(0);
int port = ss.getLocalPort();
ss.close();
String[] defaultArgs = new String[] {"-host",hostName,"-port",String.valueOf(port),"-env",labelList.toString(),"-hubURL","http://"+masterName+":"+masterPort+"/" };
PluginImpl.createSeleniumRCVM(f,listener).callAsync(
new RemoteControlLauncher((String[]) ArrayUtils.addAll(defaultArgs, userArgs.toArray(new String[0]))));
}
} catch (Exception t) {
LOGGER.log(Level.WARNING,"Selenium RC launch failed",t);
throw new IOException2("Selenium RC launch interrupted",t);
}
return null;
}
});
}
private boolean hasText(String s){
return s != null && s.trim().length() > 0;
}
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(ComputerListenerImpl.class.getName());
private static final String SEPARATOR = ",";
}