com.google.jstestdriver.FileUploader.java Source code

Java tutorial

Introduction

Here is the source code for com.google.jstestdriver.FileUploader.java

Source

/*
 * Copyright 2010 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.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.google.inject.Inject;
import com.google.inject.name.Named;
import com.google.jstestdriver.JsonCommand.CommandType;
import com.google.jstestdriver.Response.ResponseType;
import com.google.jstestdriver.browser.BrowserFileSet;
import com.google.jstestdriver.hooks.FileInfoScheme;
import com.google.jstestdriver.model.HandlerPathPrefix;
import com.google.jstestdriver.model.JstdTestCase;
import com.google.jstestdriver.model.JstdTestCaseDelta;
import com.google.jstestdriver.servlet.fileset.BrowserFileCheck;
import com.google.jstestdriver.servlet.fileset.DeltaUpload;
import com.google.jstestdriver.servlet.fileset.TestCaseUpload;
import com.google.jstestdriver.util.StopWatch;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Handles the uploading of files.
 *
 * @author corysmith@google.com (Cory Smith)
 */
public class FileUploader {
    public static final int CHUNK_SIZE = 50;

    private final StopWatch stopWatch;
    private final Gson gson = new Gson();
    private final Server server;
    private final String baseUrl;
    private final FileLoader fileLoader;
    private final JsTestDriverFileFilter filter;
    private final Set<FileInfoScheme> schemes;

    private static final Logger logger = LoggerFactory.getLogger(FileUploader.class);

    private final HandlerPathPrefix prefix;

    @Inject
    public FileUploader(StopWatch stopWatch, Server server, @Named("server") String baseUrl, FileLoader fileLoader,
            JsTestDriverFileFilter filter, Set<FileInfoScheme> schemes,
            @Named("serverHandlerPrefix") HandlerPathPrefix prefix) {
        this.stopWatch = stopWatch;
        this.server = server;
        this.baseUrl = baseUrl;
        this.fileLoader = fileLoader;
        this.filter = filter;
        this.schemes = schemes;
        this.prefix = prefix;
    }

    /** Uploads the changed files to the server and the browser. */
    public void uploadFileSet(String browserId, Collection<JstdTestCase> testCases, ResponseStream stream) {

        stopWatch.start("determineServerFileSet(%s)", browserId);
        final Collection<JstdTestCaseDelta> deltas = determineServerFileSet(testCases);
        stopWatch.stop("determineServerFileSet(%s)", browserId);

        logger.debug("Deltas: {}", deltas);
        stopWatch.start("upload to server %s", browserId);
        uploadToServer(deltas);
        stopWatch.stop("upload to server %s", browserId);
        for (JstdTestCase testCase : testCases) {
            stopWatch.start("determineBrowserFileSet(%s)", browserId);
            final List<FileInfo> browserFilesToUpdate = determineBrowserFileSet(browserId, testCase, stream);
            stopWatch.stop("determineBrowserFileSet(%s)", browserId);

            stopWatch.start("uploadToTheBrowser(%s)", browserId);
            uploadToTheBrowser(browserId, stream, browserFilesToUpdate, CHUNK_SIZE);
            stopWatch.stop("uploadToTheBrowser(%s)", browserId);
        }
    }

    /**
     * Uploads the {@link JstdTestCase}s to the server, and retrieves a list
     * of {@link JstdTestCaseDelta}s of the files that are different.
     */
    public Collection<JstdTestCaseDelta> determineServerFileSet(Collection<JstdTestCase> testCases) {
        Map<String, String> fileSetParams = new LinkedHashMap<String, String>();
        fileSetParams.put("data", gson.toJson(testCases));
        fileSetParams.put("action", TestCaseUpload.ACTION);
        String postResult = server.post(baseUrl + "/fileSet", fileSetParams);
        return gson.fromJson(postResult, new TypeToken<Collection<JstdTestCaseDelta>>() {
        }.getType());
    }

    /** Determines what files have been changed as compared to the server. */
    public List<FileInfo> determineBrowserFileSet(String browserId, JstdTestCase testCase, ResponseStream stream) {

        BrowserFileSet browserFileSet = getBrowserFileSet(browserId, testCase);

        stopWatch.start("resolving browser upload %s", browserId);
        try {
            logger.debug("Updating files {}", browserFileSet.getFilesToUpload());
            // need a linked hashset here to avoid adding a file more than once.
            final Set<FileInfo> finalFilesToUpload = new LinkedHashSet<FileInfo>();
            // reset if there are extra files in the browser
            if (browserFileSet.shouldReset() || !browserFileSet.getExtraFiles().isEmpty()) {
                reset(browserId, stream, testCase);
                browserFileSet = getBrowserFileSet(browserId, testCase);
                logger.info("second fileset {}", browserFileSet);
            }
            for (FileInfo file : browserFileSet.getFilesToUpload()) {
                finalFilesToUpload.addAll(determineInBrowserDependencies(file, testCase.getServable()));
            }
            return Lists.newArrayList(finalFilesToUpload);
        } finally {
            stopWatch.stop("resolving browser upload %s", browserId);
        }
    }

    private BrowserFileSet getBrowserFileSet(String browserId, JstdTestCase testCase) {
        stopWatch.start("get upload set %s", browserId);
        Map<String, String> fileSetParams = new LinkedHashMap<String, String>();

        fileSetParams.put("id", browserId);
        fileSetParams.put("data", gson.toJson(testCase));
        fileSetParams.put("action", BrowserFileCheck.ACTION);
        //logger.info("FileParams: {}", fileSetParams);
        String postResult = server.post(baseUrl + "/fileSet", fileSetParams);
        if (postResult.length() < 0) {
            return new BrowserFileSet(Collections.<FileInfo>emptyList(), Collections.<FileInfo>emptyList(), false);
        }
        BrowserFileSet browserFileSet = gson.fromJson(postResult, BrowserFileSet.class);
        stopWatch.stop("get upload set %s", browserId);
        return browserFileSet;
    }

    /** Uploads files to the browser. */
    public void uploadToTheBrowser(String browserId, ResponseStream stream, List<FileInfo> loadedFiles,
            int chunkSize) {
        List<FileSource> filesSrc = Lists.newLinkedList(filterFilesToLoad(loadedFiles));
        int numberOfFilesToLoad = filesSrc.size();
        logger.info("Files toupload {}",
                Lists.transform(Lists.newArrayList(loadedFiles), new Function<FileInfo, String>() {
                    @Override
                    public String apply(FileInfo in) {
                        return "\n" + in.getDisplayPath();
                    }
                }));
        for (int i = 0; i < numberOfFilesToLoad; i += chunkSize) {

            int chunkEndIndex = Math.min(i + chunkSize, numberOfFilesToLoad);
            List<String> loadParameters = new LinkedList<String>();
            List<FileSource> filesToLoad = filesSrc.subList(i, chunkEndIndex);
            loadParameters.add(gson.toJson(filesToLoad));
            loadParameters.add("false");
            JsonCommand cmd = new JsonCommand(CommandType.LOADTEST, loadParameters);
            Map<String, String> loadFileParams = new LinkedHashMap<String, String>();

            loadFileParams.put("id", browserId);
            loadFileParams.put("data", gson.toJson(cmd));
            if (logger.isDebugEnabled()) {
                logger.debug("Sending LOADTEST to {} for {}", browserId,
                        Lists.transform(filesToLoad, new Function<FileSource, String>() {
                            @Override
                            public String apply(FileSource in) {
                                return "\n" + in.getFileSrc();
                            }
                        }));
            }
            server.post(baseUrl + "/cmd", loadFileParams);
            while (true) {
                String jsonResponse = server.fetch(baseUrl + "/cmd?id=" + browserId);
                StreamMessage message = gson.fromJson(jsonResponse, StreamMessage.class);
                Response response = message.getResponse();
                logger.trace("LOADTEST response for {}", response);
                stream.stream(response);
                if (message.isLast()) {
                    logger.debug("Finished LOADTEST on {} with {}", browserId, response.getResponseType());
                    break;
                }
            }
        }
    }

    public void uploadToServer(final Collection<JstdTestCaseDelta> deltas) {
        if (deltas.isEmpty()) {
            return;
        }
        List<JstdTestCaseDelta> loadedDeltas = Lists.newArrayListWithCapacity(deltas.size());
        for (JstdTestCaseDelta delta : deltas) {
            loadedDeltas.add(delta.loadFiles(fileLoader));
        }
        for (List<JstdTestCaseDelta> partition : Lists.partition(loadedDeltas, 50)) {
            Map<String, String> uploadFileParams = new LinkedHashMap<String, String>();
            uploadFileParams.put("action", DeltaUpload.ACTION);
            uploadFileParams.put("data", gson.toJson(partition));
            server.post(baseUrl + "/fileSet", uploadFileParams);
        }
    }

    private void reset(String browserId, ResponseStream stream, JstdTestCase testCase) {
        stopWatch.start("reset %s", browserId);
        JsonCommand cmd = new JsonCommand(CommandType.RESET, Lists.newArrayList("preload", testCase.getId()));
        Map<String, String> resetParams = new LinkedHashMap<String, String>();

        logger.debug("reset browser {}  testcase {}", browserId, testCase.getId());
        resetParams.put("id", browserId);
        resetParams.put("data", gson.toJson(cmd));
        server.post(baseUrl + "/cmd", resetParams);

        logger.trace("starting reset for {}", browserId);
        Response response;
        StreamMessage message;
        do {
            String jsonResponse = server.fetch(baseUrl + "/cmd?id=" + browserId);
            message = gson.fromJson(jsonResponse, StreamMessage.class);
            response = message.getResponse();
            stream.stream(response);
        } while (!(ResponseType.RESET_RESULT.equals(response.getResponseType()) && message.isLast()));
        logger.trace("finished reset for {}", browserId);
        stopWatch.stop("reset %s", browserId);
    }

    /**
     * Determines what files must be reloaded in the browser, based on this file
     * being updated.
     */
    private Collection<FileInfo> determineInBrowserDependencies(FileInfo file, List<FileInfo> files) {
        LinkedHashSet<FileInfo> deps = Sets.newLinkedHashSet();
        for (FileInfo dep : filter.resolveFilesDeps(file, files)) {
            deps.add(dep);
        }
        return deps;
    }

    private List<FileSource> filterFilesToLoad(Collection<FileInfo> fileInfos) {
        List<FileSource> filteredFileSources = new LinkedList<FileSource>();

        for (FileInfo fileInfo : fileInfos) {
            if (!fileInfo.isServeOnly()) {
                filteredFileSources.add(fileInfo.toFileSource(prefix, schemes));
            }
        }
        return filteredFileSources;
    }
}