/* * Copyright 2009 Google Inc. * * 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.google.jstestdriver; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import com.google.jstestdriver.JsonCommand.CommandType; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Timer; import java.util.TimerTask; /** * @author jeremiele@google.com (Jeremie Lenfant-Engelmann) */ public class CommandTask { private static final List<String> EMPTY_ARRAYLIST = new ArrayList<String>(); private static final long WAIT_INTERVAL = 500L; private final Gson gson = new Gson(); private final JsTestDriverFileFilter filter; private final ResponseStream stream; private final Set<FileInfo> fileSet; private final String baseUrl; private final Server server; private final Map<String, String> params; public CommandTask(JsTestDriverFileFilter filter, ResponseStream stream, Set<FileInfo> fileSet, String baseUrl, Server server, Map<String, String> params) { this.filter = filter; this.stream = stream; this.fileSet = fileSet; this.baseUrl = baseUrl; this.server = server; this.params = params; } private String startSession() { String browserId = params.get("id"); String sessionId = server.startSession(baseUrl, browserId); if (sessionId.equals("FAILED")) { while (sessionId.equals("FAILED")) { try { Thread.sleep(WAIT_INTERVAL); } catch (InterruptedException e) { System.err.println("Could not create session for browser: " + browserId); return ""; } sessionId = server.startSession(baseUrl, browserId); } } return sessionId; } private void stopSession(String sessionId) { server.stopSession(baseUrl, params.get("id"), sessionId); } private boolean isBrowserAlive() { String alive = server.fetch(baseUrl + "/heartbeat?id=" + params.get("id")); if (!alive.equals("OK")) { System.err.println("The browser " + params.get("id") + " is not available anymore, " + "you might want to re-capture it"); return false; } return true; } private void uploadFileSet() { Map<String, String> fileSetParams = new LinkedHashMap<String, String>(); Map<String, List<String>> patchMap = new HashMap<String, List<String>>(); List<FileInfo> patchLessFileSet = createPatchLessFileSet(fileSet, patchMap); fileSetParams.put("id", params.get("id")); fileSetParams.put("fileSet", gson.toJson(patchLessFileSet)); String postResult = server.post(baseUrl + "/fileSet", fileSetParams); if (postResult.length() > 0) { Collection<String> filesToUpload = gson.fromJson(postResult, new TypeToken<Collection<String>>() {}.getType()); boolean shouldReset = sameFiles(filesToUpload, patchLessFileSet); if (shouldReset) { JsonCommand cmd = new JsonCommand(CommandType.RESET, EMPTY_ARRAYLIST); Map<String, String> resetParams = new LinkedHashMap<String, String>(); resetParams.put("id", params.get("id")); resetParams.put("data", gson.toJson(cmd)); server.post(baseUrl + "/cmd", resetParams); server.fetch(baseUrl + "/cmd?id=" + params.get("id")); } List<FileData> filesData = new ArrayList<FileData>(); List<String> filesSrc = new ArrayList<String>(); Set<String> finalFilesToUpload = new LinkedHashSet<String>(); for (String file : filesToUpload) { finalFilesToUpload.addAll(filter.resolveFilesDeps(file)); } for (String file : finalFilesToUpload) { StringBuilder fileContent = new StringBuilder(); long timestamp = -1; if (file.startsWith("http://") || file.startsWith("https://")) { filesSrc.add(file); fileContent.append("none"); } else { filesSrc.add("/test/" + file); fileContent.append(filter.filterFile(readFile(file), !shouldReset)); List<String> patches = patchMap.get(file); if (patches != null) { for (String patch : patches) { fileContent.append(readFile(patch)); } } timestamp = getTimestamp(file); } filesData.add(new FileData(file, fileContent.toString(), timestamp)); } Map<String, String> loadFileParams = new LinkedHashMap<String, String>(); loadFileParams.put("id", params.get("id")); loadFileParams.put("data", gson.toJson(filesData)); server.post(baseUrl + "/fileSet", loadFileParams); JsonCommand cmd = new JsonCommand(CommandType.LOADTEST, filesSrc); loadFileParams.put("data", gson.toJson(cmd)); server.post(baseUrl + "/cmd", loadFileParams); String jsonResponse = server.fetch(baseUrl + "/cmd?id=" + params.get("id")); StreamMessage message = gson.fromJson(jsonResponse, StreamMessage.class); Response response = message.getResponse(); LoadedFiles loadedFiles = gson.fromJson(response.getResponse(), LoadedFiles.class); String loadStatus = loadedFiles.getMessage(); if (loadStatus.length() > 0) { System.err.println(loadStatus); } } } private List<FileInfo> createPatchLessFileSet(Set<FileInfo> originalFileSet, Map<String, List<String>> patchMap) { List<FileInfo> patchLessFileSet = new LinkedList<FileInfo>(); for (FileInfo i : originalFileSet) { if (i.isPatch()) { int size = patchLessFileSet.size(); if (size > 0) { FileInfo prev = patchLessFileSet.get(size - 1); List<String> patches = patchMap.get(prev.getFileName()); if (patches == null) { patches = new LinkedList<String>(); patchMap.put(prev.getFileName(), patches); } patches.add(i.getFileName()); } else { patchLessFileSet.add(i); } } else { patchLessFileSet.add(i); } } return patchLessFileSet; } public void run() { Timer timer = new Timer(true); String browserId = params.get("id"); String sessionId = null; try { sessionId = startSession(); if (!sessionId.equals("")) { timer.schedule(new HeartBeat( (baseUrl + "/fileSet?id=" + browserId + "&sessionId=" + sessionId)), 0, 500); } if (!isBrowserAlive()) { return; } uploadFileSet(); server.post(baseUrl + "/cmd", params); StreamMessage streamMessage = null; do { String response = server.fetch(baseUrl + "/cmd?id=" + browserId); streamMessage = gson.fromJson(response, StreamMessage.class); stream.stream(streamMessage.getResponse()); } while (streamMessage != null && !streamMessage.isLast()); } finally { stream.finish(); timer.cancel(); stopSession(sessionId); } } private static class HeartBeat extends TimerTask { private final String url; public HeartBeat(String url) { this.url = url; } private String toString(InputStream inputStream) throws IOException { StringBuilder sb = new StringBuilder(); int ch; while ((ch = inputStream.read()) != -1) { sb.append((char) ch); } inputStream.close(); return sb.toString(); } @Override public void run() { HttpURLConnection connection = null; try { connection = (HttpURLConnection) new URL(url).openConnection(); connection.connect(); toString(connection.getInputStream()); } catch (MalformedURLException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } finally { if (connection != null) { connection.disconnect(); } } } } private long getTimestamp(String file) { for (FileInfo info : fileSet) { if (info.getFileName().equals(file)) { return info.getTimestamp(); } } return 0; } private boolean sameFiles(Collection<String> filesToUpload, List<FileInfo> fileSet) { if (filesToUpload.size() != fileSet.size()) { return false; } for (FileInfo info : fileSet) { if (!filesToUpload.contains(info.getFileName())) { return false; } } return true; } private String readFile(String file) { BufferedInputStream bis = null; try { bis = new BufferedInputStream(new FileInputStream(file)); StringBuilder sb = new StringBuilder(); for (int c = bis.read(); c != -1; c = bis.read()) { sb.append((char) c); } return sb.toString(); } catch (IOException e) { throw new RuntimeException("Impossible to read file: " + file, e); } finally { if (bis != null) { try { bis.close(); } catch (IOException e) { // ignore } } } } }