org.centralperf.controller.ScriptController.java Source code

Java tutorial

Introduction

Here is the source code for org.centralperf.controller.ScriptController.java

Source

/*
 * Copyright (C) 2014  The Central Perf authors
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.centralperf.controller;

import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;

import javax.annotation.Resource;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.centralperf.controller.exception.ControllerValidationException;
import org.centralperf.model.LastScriptVersionLabel;
import org.centralperf.model.dao.Project;
import org.centralperf.model.dao.Run;
import org.centralperf.model.dao.Script;
import org.centralperf.model.dao.ScriptVersion;
import org.centralperf.repository.ProjectRepository;
import org.centralperf.repository.RunRepository;
import org.centralperf.repository.ScriptRepository;
import org.centralperf.repository.ScriptVersionRepository;
import org.centralperf.sampler.api.Sampler;
import org.centralperf.service.SamplerService;
import org.centralperf.service.ScriptService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;

/**
 * The Script controller handle user interactions with the scripts and their versions
 *  
 * @since 1.0
 * 
 */
@Controller
@SessionAttributes
public class ScriptController extends BaseController {

    @Resource
    private ScriptRepository scriptRepository;

    @Resource
    private ScriptVersionRepository scriptVersionRepository;

    @Resource
    private ScriptService scriptService;

    @Resource
    private SamplerService samplerService;

    @Resource
    private RunRepository runRepository;

    @Resource
    private ProjectRepository projectRepository;

    private static final Logger log = LoggerFactory.getLogger(ScriptController.class);

    /**
     * Generic Exception handler for the whole controller
     * @param exception   The exception raised
     * @return The message of the exception
     */
    @ExceptionHandler(Throwable.class)
    public @ResponseBody String handleValidationFailure(Throwable exception) {
        return exception.getMessage();
    }

    /**
     * Add a new script to a project
     * @param projectId   ID of the project
     * @param script   Script to add (from HTML form binding)
     * @param file   Uploaded file of the script (JMX or other)
     * @return Redirection to the script detail page
     */
    @RequestMapping(value = "/project/{projectId}/script/new", method = RequestMethod.POST)
    public String addScript(@PathVariable("projectId") Long projectId, @ModelAttribute("script") Script script,
            @RequestParam("jmxFile") MultipartFile file) {
        String scriptContent = null;

        // Get the script File
        try {
            scriptContent = new String(file.getBytes());
        } catch (IOException e) {
        }
        Script newScript = scriptService.addScript(script.getProject(), script.getSamplerUID(), script.getLabel(),
                script.getDescription(), scriptContent);
        return "redirect:/project/" + projectId + "/script/" + newScript.getId() + "/detail";
    }

    /**
     * Add a version to a script
     * @param projectId   ID of the project
     * @param scriptId   ID of the script
     * @param scriptVersion   Script version to add (from HTML form binding)
     * @param file Uploaded file of the script (JMX or other)
     * @return Redirection to the script detail page
     */
    @RequestMapping(value = "/project/{projectId}/script/{scriptId}/version/new", method = RequestMethod.POST)
    public String addScriptVersion(@PathVariable("projectId") Long projectId,
            @PathVariable("scriptId") Long scriptId, @ModelAttribute("scriptVersion") ScriptVersion scriptVersion,
            @RequestParam("scriptFile") MultipartFile file) {
        String scriptContent = null;

        // Get the script File
        try {
            scriptContent = new String(file.getBytes());
        } catch (IOException e) {
        }
        ScriptVersion newScriptVersion = scriptService.addScriptVersion(scriptRepository.findOne(scriptId),
                scriptVersion.getDescription(), scriptContent);
        return "redirect:/project/" + projectId + "/script/" + scriptId + "/detail#" + newScriptVersion.getId();
    }

    /**
     * Display all script for a project
     * @param projectId   ID of the project
     * @param model   Model prepared for the script listing view
     * @return Name of the scripts listing view
     */
    @RequestMapping("/project/{projectId}/script")
    public String showProjectScripts(@PathVariable("projectId") Long projectId, Model model) {
        model.addAttribute("newScript", new Script());
        model.addAttribute("scripts", scriptRepository.findAll());
        model.addAttribute("project", projectRepository.findOne(projectId));
        return "scripts";
    }

    /**
     * List scripts for a project as JSON
     * @param projectId   ID of the project
     * @return   The script list that will be converted to JSON
     */
    @RequestMapping(value = "/project/{projectId}/script/json/list", method = RequestMethod.GET)
    public @ResponseBody List<LastScriptVersionLabel> getJsonScriptList(@PathVariable("projectId") Long projectId) {
        Project p = projectRepository.findOne(projectId);
        List<LastScriptVersionLabel> lst = new ArrayList<LastScriptVersionLabel>();
        for (Script s : p.getScripts()) {
            List<ScriptVersion> versions = scriptVersionRepository.findByScriptIdOrderByNumberDesc(s.getId());
            ScriptVersion scriptVersion = versions.get(0);
            lst.add(new LastScriptVersionLabel(scriptVersion.getId(), s.getLabel() + " (version "
                    + scriptVersion.getNumber() + ")" + " - " + scriptVersion.getScript().getSamplerUID()));
        }
        return lst;
    }

    /**
     * Display the detail of a script
     * @param scriptId   ID of the script
     * @param projectId   ID of the project
     * @param model   Model prepared for the script detail view
     * @return Name of the script detail view
     */
    @RequestMapping(value = "/project/{projectId}/script/{scriptId}/detail", method = RequestMethod.GET)
    public String showScriptDetail(@PathVariable("scriptId") Long scriptId,
            @PathVariable("projectId") Long projectId, Model model) {
        model.addAttribute("script", scriptRepository.findOne(scriptId));
        model.addAttribute("project", projectRepository.findOne(projectId));
        model.addAttribute("scriptVersions", scriptVersionRepository.findByScriptIdOrderByNumberDesc(scriptId));
        model.addAttribute("newScriptVersion", new ScriptVersion());
        return "scriptDetail";
    }

    /**
     * Delete a script
     * @param scriptId   ID of the script to delete
     * @param projectId   ID of the project
     * @param redirectAttrs   Attributes to display in case of errors
     * @return   Redirection to the script list page for the project
     */
    @RequestMapping(value = "/project/{projectId}/script/{id}/delete", method = RequestMethod.GET)
    public String deleteScript(@PathVariable("id") Long scriptId, @PathVariable("projectId") Long projectId,
            RedirectAttributes redirectAttrs) {
        List<Run> runsAttached = runRepository.findByScriptVersionScriptId(scriptId);
        if (!runsAttached.isEmpty()) {
            redirectAttrs.addFlashAttribute("error",
                    "Unable to delete this script because it's attached to at least one run");
        } else {
            scriptRepository.delete(scriptId);
        }
        return "redirect:/project/" + projectId + "/script";
    }

    /**
     * Delete a script version
     * @param projectId   ID of the project
     * @param scriptId   ID of the script
     * @param versionId   ID of the version
     * @param redirectAttrs      Attributes to display in case of errors
     * @return Redirection to the script detail page after deletion
     */
    @RequestMapping(value = "/project/{projectId}/script/{scriptId}/version/{versionId}/delete", method = RequestMethod.GET)
    public String deleteScriptVersion(@PathVariable("projectId") Long projectId,
            @PathVariable("scriptId") Long scriptId, @PathVariable("versionId") Long versionId,
            RedirectAttributes redirectAttrs) {
        List<Run> runsAttached = runRepository.findByScriptVersionId(versionId);
        if (!runsAttached.isEmpty()) {
            redirectAttrs.addFlashAttribute("error",
                    "Unable to delete this version because it's attached to at least one run");
        } else {
            scriptService.deleteScriptVersion(versionId);
        }
        return "redirect:/project/" + projectId + "/script/" + scriptId + "/detail";
    }

    /**
     * Download the content of a script (content is only stored on the version level)
     * @param projectId   ID of the project
     * @param scriptId   ID of the script
     * @param versionId   ID of the version
     * @return the script content as application/octet-stream content (to download it instead of displaying it in the browser)
     */
    @RequestMapping(value = "/project/{projectId}/script/{scriptId}/version/{versionId}/download", method = RequestMethod.GET, produces = "application/octet-stream")
    public ResponseEntity<String> downloadScriptVersionContent(@PathVariable("projectId") Long projectId,
            @PathVariable("scriptId") Long scriptId, @PathVariable("versionId") Long versionId) {
        ScriptVersion scriptVersion = scriptVersionRepository.findOne(versionId);
        Sampler sampler = samplerService.getSamplerByUID(scriptVersion.getScript().getSamplerUID());
        HttpHeaders responseHeaders = new HttpHeaders();
        // Build the file name based on script label and script version number
        responseHeaders.set("Content-Disposition",
                "attachment; filename=" + scriptVersion.getScript().getLabel().replaceAll("\\s+", "_") + "_"
                        + scriptVersion.getNumber() + sampler.getScriptFileExtension());
        return new ResponseEntity<String>(scriptVersion.getContent(), responseHeaders, HttpStatus.CREATED);
    }

    /**
     * List all scripts for all projects
     * @param model Model prepared for all scripts list view
     * @return The name of the all scripts view
     */
    @RequestMapping("/script")
    public String showScripts(Model model) {
        model.addAttribute("scripts", scriptRepository.findAll());
        return "scripts";
    }

    /**
     * Update a script (label or description)
     * @param scriptId   ID of the script to update
     * @param label   New label (not updated if null)
     * @param description   New description (not updated if null)
     * @return The new value of the attribute that has been updated
     * @throws ControllerValidationException 
     */
    @RequestMapping(value = "/script/{scriptId}", method = RequestMethod.POST)
    @ResponseBody
    public String updateScript(@PathVariable("scriptId") Long scriptId,
            @RequestParam(value = "label", required = false) String label,
            @RequestParam(value = "description", required = false) String description)
            throws ControllerValidationException {
        Script script = scriptRepository.findOne(scriptId);
        String valueToReturn = label;
        if (label != null) {
            script.setLabel(label);
        } else if (description != null) {
            script.setDescription(description);
            valueToReturn = description;
        }

        validateBean(script);

        scriptRepository.save(script);
        return valueToReturn;
    }

    /**
     * Update a script version (description)
     * @param scriptId   ID of the script to update
     * @param scriptVersionId   ID of the script version to update
     * @param description   New description (not updated if null)
     * @return The new value of the attribute that has been updated
     * @throws ControllerValidationException 
     */
    @RequestMapping(value = "/script/{scriptId}/version/{scriptVersionId}", method = RequestMethod.POST)
    @ResponseBody
    public String updateScriptVersion(@PathVariable("scriptId") Long scriptId,
            @PathVariable("scriptVersionId") Long scriptVersionId,
            @RequestParam(value = "description", required = false) String description)
            throws ControllerValidationException {
        ScriptVersion scriptVersion = scriptVersionRepository.findOne(scriptVersionId);
        String valueToReturn = description;
        if (description != null) {
            scriptVersion.setDescription(description);
            valueToReturn = description;
        }

        validateBean(scriptVersion);

        scriptVersionRepository.save(scriptVersion);
        return valueToReturn;
    }

    // TODO preview should be generated by the injector driver (jMeter or Gatling)
    /**
     * Preview a script (only for JMX). The preview is based on an XSL transformation applied to the JMX XML file, allowing to display it as a javascript tree
     * @param model   Model prepared for the script preview view
     * @param scriptId   ID of the script to preview
     * @param versionNumber
     * @return The name of the XSL view to use
     */
    @RequestMapping(value = "/script/{scriptId}/version/{versionNumber}/preview")
    public String previewScript(Model model, @PathVariable("scriptId") Long scriptId,
            @PathVariable("versionNumber") int versionNumber) {
        Script script = scriptRepository.findOne(scriptId);
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        DocumentBuilder builder;
        try {
            builder = factory.newDocumentBuilder();
            Document doc = builder.parse(
                    new InputSource(new StringReader(script.getVersions().get(versionNumber - 1).getContent())));
            model.addAttribute("obj", doc);
        } catch (Exception e) {
            log.error("A problem occured while generating the preview of the script " + e.getMessage());
            e.printStackTrace();
        }

        // Parse JMX File
        return "jmeter-test-plan-preview.xsl";
    }
}