/* * 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 org.ngrinder.script.handler; import freemarker.template.Configuration; import freemarker.template.DefaultObjectWrapper; import freemarker.template.Template; import org.apache.commons.io.FilenameUtils; import org.ngrinder.common.constant.ControllerConstants; import org.ngrinder.common.util.FileUtils; import org.ngrinder.common.util.PathUtils; import org.ngrinder.common.util.PropertiesWrapper; import org.ngrinder.model.User; import org.ngrinder.script.model.FileEntry; import org.ngrinder.script.model.FileType; import org.ngrinder.script.repository.FileEntryRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.ClassPathResource; import java.io.File; import java.io.StringWriter; import java.util.List; import java.util.Map; import static org.apache.commons.lang.StringUtils.startsWithIgnoreCase; import static org.ngrinder.common.util.CollectionUtils.newArrayList; import static org.ngrinder.common.util.ExceptionUtils.processException; /** * Script per language handler. This is the superclass for all sub * {@link ScriptHandler}s which implements the specific processing of each * language. * * @author JunHo Yoon * @since 3.2 */ public abstract class ScriptHandler implements ControllerConstants { protected static final Logger LOGGER = LoggerFactory.getLogger(JythonScriptHandler.class); private final String codemirrorKey; private final String title; private final String extension; private final String key; /** * Constructor. * * @param key key of the script handler * @param extension extension * @param title title of the handler * @param codeMirrorKey code mirror key */ public ScriptHandler(String key, String extension, String title, String codeMirrorKey) { this.key = key; this.extension = extension; this.title = title; this.codemirrorKey = codeMirrorKey; } @Autowired private FileEntryRepository fileEntryRepository; /** * Get the display order of {@link ScriptHandler}s. * * @return order */ public abstract Integer displayOrder(); public String getCodemirrorKey() { return codemirrorKey; } /** * Check if the given fileEntry can be handled by this handler. * * @param fileEntry fileEntry to be checked * @return true if the given fileEntry can be handled */ public boolean canHandle(FileEntry fileEntry) { return FilenameUtils.isExtension(fileEntry.getPath(), getExtension()); } public String getExtension() { return extension; } /** * Get the handler resolution order. * <p/> * Less is more prioritized. * * @return the order of handler resolution */ protected abstract Integer order(); @SuppressWarnings("SpellCheckingInspection") public boolean isValidatable() { return true; } /** * Return if it's project handler which implements {@link ProjectHandler}. * * @return true if it is. */ @SuppressWarnings("UnusedDeclaration") public boolean isProjectHandler() { return (this instanceof ProjectHandler); } /** * Prepare the distribution. * * @param testCaseId id of the test case. This is for the log identification. * @param user user who will distribute the script. * @param scriptEntry script to be distributed. * @param distDir distribution target dir. * @param properties properties set which is used for detailed distribution control. * @param processingResult processing result holder. */ public void prepareDist(Long testCaseId, User user, // FileEntry scriptEntry, File distDir, PropertiesWrapper properties, ProcessingResultPrintStream processingResult) { prepareDefaultFile(distDir, properties); List<FileEntry> fileEntries = getLibAndResourceEntries(user, scriptEntry, -1); if (scriptEntry.getRevision() != 0) { fileEntries.add(scriptEntry); } String basePath = getBasePath(scriptEntry); // Distribute each files in that folder. for (FileEntry each : fileEntries) { // Directory is not subject to be distributed. if (each.getFileType() == FileType.DIR) { continue; } File toDir = new File(distDir, calcDistSubPath(basePath, each)); processingResult.printf("%s is being written.\n", each.getPath()); LOGGER.info("{} is being written in {} for test {}", new Object[]{each.getPath(), toDir, testCaseId}); getFileEntryRepository().writeContentTo(user, each.getPath(), toDir); } processingResult.setSuccess(true); prepareDistMore(testCaseId, user, scriptEntry, distDir, properties, processingResult); } /** * Prepare script creation. This method is subject to be extended by the * subclasses. * <p/> * This method is the perfect place if it's necessary to include additional * files. * * @param user user * @param path base path * @param fileName fileName * @param name name * @param url url * @param createLibAndResources true if lib and resources should be created * @return true if process more. */ public boolean prepareScriptEnv(User user, String path, String fileName, String name, String url, boolean createLibAndResources, String scriptContent) { return true; } /** * Prepare the distribution more. This method is subject to be extended by * the subclass. * * @param testCaseId test case id. This is for the log identification. * @param user user * @param script script entry to be distributed. * @param distDir distribution directory * @param properties properties * @param processingResult processing result holder */ protected void prepareDistMore(Long testCaseId, User user, FileEntry script, File distDir, PropertiesWrapper properties, ProcessingResultPrintStream processingResult) { } /** * Get the appropriated distribution path for the given file entry. * * @param basePath distribution base path * @param fileEntry fileEntry to be distributed * @return the resolved destination path. */ protected String calcDistSubPath(String basePath, FileEntry fileEntry) { String path = FilenameUtils.getPath(fileEntry.getPath()); path = path.substring(basePath.length()); return path; } /** * Get all resources and lib entries belonging to the given user and * scriptEntry. * * @param user user * @param scriptEntry script entry * @param revision revision of the script entry. * @return file entry list */ public List<FileEntry> getLibAndResourceEntries(User user, FileEntry scriptEntry, long revision) { String path = FilenameUtils.getPath(scriptEntry.getPath()); List<FileEntry> fileList = newArrayList(); for (FileEntry eachFileEntry : getFileEntryRepository().findAll(user, path + "lib/", revision, true)) { // Skip jython 2.5... it's already included. if (startsWithIgnoreCase(eachFileEntry.getFileName(), "jython-2.5.") || startsWithIgnoreCase(eachFileEntry.getFileName(), "jython-standalone-2.5.")) { continue; } FileType fileType = eachFileEntry.getFileType(); if (fileType.isLibDistributable()) { fileList.add(eachFileEntry); } } for (FileEntry eachFileEntry : getFileEntryRepository().findAll(user, path + "resources/", revision, true)) { FileType fileType = eachFileEntry.getFileType(); if (fileType.isResourceDistributable()) { fileList.add(eachFileEntry); } } return fileList; } protected void prepareDefaultFile(File distDir, PropertiesWrapper properties) { if (properties.getPropertyBoolean(PROP_CONTROLLER_DIST_LOGBACK)) { FileUtils.copyResourceToFile("/logback/logback-worker.xml", new File(distDir, "logback-worker.xml")); } } protected String getBasePath(FileEntry script) { return getBasePath(script.getPath()); } /** * Get the base path of the given path. * * @param path path * @return base path */ public String getBasePath(String path) { return FilenameUtils.getPath(path); } /** * Get executable script path. * * @param svnPath path in svn * @return path executable in agent. */ public String getScriptExecutePath(String svnPath) { return FilenameUtils.getName(svnPath); } /** * Check syntax errors for the given content. * * @param path path * @param content content * @return syntax error messages. null if none. */ public abstract String checkSyntaxErrors(String path, String content); /** * Get the initial script with the given value map. * * @param values map of initial script referencing values. * @return generated string */ public String getScriptTemplate(Map<String, Object> values) { try { Configuration freemarkerConfig = new Configuration(); ClassPathResource cpr = new ClassPathResource("script_template"); freemarkerConfig.setDirectoryForTemplateLoading(cpr.getFile()); freemarkerConfig.setObjectWrapper(new DefaultObjectWrapper()); Template template = freemarkerConfig.getTemplate("basic_template_" + getExtension() + ".ftl"); StringWriter writer = new StringWriter(); template.process(values, writer); return writer.toString(); } catch (Exception e) { throw processException("Error while fetching the script template.", e); } } public String getTitle() { return title; } public String getKey() { return key; } FileEntryRepository getFileEntryRepository() { return fileEntryRepository; } void setFileEntryRepository(FileEntryRepository fileEntryRepository) { this.fileEntryRepository = fileEntryRepository; } /** * Get the default quick test file. * * @param basePath base path * @return quick test file */ public FileEntry getDefaultQuickTestFilePath(String basePath) { FileEntry fileEntry = new FileEntry(); fileEntry.setPath(PathUtils.join(basePath, "TestRunner." + getExtension())); return fileEntry; } }