diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 0000000000..41021c0779 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,33 @@ +--- +name: Bug report +about: Create a bug & errors report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +### 1. Describe the bug ๐Ÿž +:exclamation: DO NOT ASK QUESTION HERE PLEASE USE [DISCUSSION](https://github.com/naver/ngrinder/discussions) INSTEAD. + +A clear and concise description of what the bug is. + +### 2. Reproduction steps +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +### 3. Environment +- Controller + - OS: + - Browser: + - JDK version: + - Controller version: +- Agent + - OS: + - JDK version: + - Agent version: + +### 4. Screenshots +If necessary, add screenshots to help explain your problem. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..fddb373fb5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: nGrinder community forum + url: https://github.com/naver/ngrinder/discussions + about: Please ask and answer questions here. diff --git a/NOTICE b/NOTICE index 9264b5affa..478f00e6f4 100644 --- a/NOTICE +++ b/NOTICE @@ -28,6 +28,10 @@ The following components are included without modification: Information: http://logging.apache.org/ License: http://www.apache.org/licenses/LICENSE-2.0 +- httpcomponents-client - +Information: https://github.com/apache/httpcomponents-client +License: http://www.apache.org/licenses/LICENSE-2.0 + The following components are included with modification: - cpptasks - @@ -554,3 +558,26 @@ See the License for the specific language governing permissions and limitations under the License. ===== + +apache/httpcomponents-client +https://github.com/apache/httpcomponents-client + +Apache HttpComponents Client +Copyright 1999-2021 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + +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. + +===== diff --git a/README.md b/README.md index ace2e7a244..3fef3e2ddc 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -* nGrinder 3.5.3 has been released. See https://github.com/naver/ngrinder/releases +* Please post questions in [Discussions](https://github.com/naver/ngrinder/discussions) not Issues. +* nGrinder 3.5.4 has been released. See https://github.com/naver/ngrinder/releases nGrinder ======== diff --git a/RELEASE-NOTE.md b/RELEASE-NOTE.md index 7d0ebe6b50..eb90b803f3 100644 --- a/RELEASE-NOTE.md +++ b/RELEASE-NOTE.md @@ -1,3 +1,31 @@ +3.5.4 (2021.03.19) +================== +- Changes + - Release new experimental HttpClient based on apache httpcomponents-core + * Support modern HTTP specification + * Provide easy-to-use APIs + * See [new nGrinder HTTP client guide](https://github.com/naver/ngrinder/wiki/The-New-nGrinder-HTTP-Client) + - Use controller DNS instead of IP to support agent-controller reconnection in cloud env + - Support script template customization + * Each ngrinder admin can deploy customized script template under ${NGRINDER_HOME}/script_template + - Provide the connection reset option in test configuration page + * If you turn it on, it tries to reuse connection as much as possible. + - Bump Jython standard up to 2.7.2 + - Bump internal used Junit up to 4.13.1 + * nGrinder no longer depends on specific version of junit for performance test +- Notice + - If you have a trouble with updating controller from ngrinder 3.4.X to a newer version. Please refer to [How to update ngrinder controller](https://github.com/naver/ngrinder/wiki/How-to-update-ngrinder-controller) +- Bug fix + - #706 Cancel progressing state test when starting controller + - #714 Fix validation error when using AppClassLoader in over JDK9 + - #716 Fix script validation error in windows + - #731 Fix not working remember-me + - #739 Fix easy clustering + - #745 Make compatibility with IE11 + - #748 Make grinder utils work on Jython performance test + - #773 Fix cannot search user with two characters + - #774, #779 Fix duplicated running test on one user + 3.5.3 (2020.11.27) ================== - Changes diff --git a/build.gradle b/build.gradle index 0a93d11f2c..70d3a036ba 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ allprojects { apply plugin: "idea" group = "org.ngrinder" - version = "3.5.3" + version = "3.5.4" idea { module { @@ -35,6 +35,7 @@ subprojects { ext { profile = project.hasProperty("profile") ? profile : "production" + esCheckModuleVersion = project.hasProperty("esCheckModuleVersion") ? esCheckModuleVersion : "" slf4j_version = "1.7.28" spring_security_version = "5.3.4.RELEASE" spring_boot_version = "2.3.3.RELEASE" @@ -44,7 +45,8 @@ subprojects { mockito_version = "2.23.4" handlebars_version = "4.0.5" jackson_version = "2.11.2" - groovy_version = "3.0.5" + groovy_version = project.property("groovy.version") + junit_version = project.property("junit.version") } repositories { diff --git a/docker/agent/Dockerfile b/docker/agent/Dockerfile index 45a4bc4d84..88a2b2eac2 100644 --- a/docker/agent/Dockerfile +++ b/docker/agent/Dockerfile @@ -5,6 +5,8 @@ RUN apk update; apk add curl bash # Set up environment variables +ENV JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 +ENV LANG=en_US.UTF-8 ENV BASE_DIR=/opt \ NGRINDER_AGENT_BASE=/opt/ngrinder-agent \ NGRINDER_AGENT_HOME=/opt/ngrinder-agent/.ngrinder-agent diff --git a/docker/controller/Dockerfile b/docker/controller/Dockerfile index 7543c487f6..b5aecce49c 100644 --- a/docker/controller/Dockerfile +++ b/docker/controller/Dockerfile @@ -26,6 +26,8 @@ RUN mkdir -p /usr/share/gradle \ && rm -f /tmp/gradle.zip # Set up environment variables +ENV JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8 +ENV LANG=en_US.UTF-8 ENV BASE_DIR=/opt \ NGRINDER_HOME=/opt/ngrinder-controller \ MAVEN_HOME=/usr/share/maven \ diff --git a/gradle.properties b/gradle.properties index 6f7146c3da..51c3f0393c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,4 @@ org.gradle.daemon=true jna.version=5.6.0 groovy.version=3.0.5 +junit.version=4.13.1 diff --git a/ngrinder-controller/build.gradle b/ngrinder-controller/build.gradle index 16d88f1076..42093c0a00 100644 --- a/ngrinder-controller/build.gradle +++ b/ngrinder-controller/build.gradle @@ -40,9 +40,10 @@ dependencies { compile (group: "rome", name: "rome", version: "1.0") compile (group: "com.ibm.icu", name: "icu4j", version: "4.6") compile (group: "sonia.svnkit", name: "svnkit-dav", version: svnkit_version) - compile (group: "sonia.svnkit", name: "svnkit", version: svnkit_version) + compile (group: "sonia.svnkit", name: "svnkit", version: svnkit_version) { + exclude (module: "platform") + } compile (group: "javax.servlet.jsp", name: "jsp-api", version: "2.1") - compile (group: "org.python", name: "jython-standalone", version: "2.5.3") compile (group: "com.google.guava", name: "guava", version: "20.0") compile (group: "org.springframework.security", name: "spring-security-taglibs", version: spring_security_version) compile (group: "org.liquibase", name: "liquibase-core", version: "3.5.3") @@ -54,7 +55,6 @@ dependencies { compile (group: "com.h2database", name: "h2", version: "1.4.197") compile (group: "commons-fileupload", name: "commons-fileupload", version: "1.3.1") compile (group: "commons-dbcp", name: "commons-dbcp", version: "1.4") - compile (group: "cglib", name: "cglib", version: "2.2.2") compile (group: "com.fasterxml.jackson.core", name: "jackson-annotations", version: jackson_version) compile (group: "com.fasterxml.jackson.core", name: "jackson-databind", version: jackson_version) compile (group: "jaxen", name: "jaxen", version: "1.1.4") @@ -75,10 +75,9 @@ dependencies { providedRuntime (group: "org.springframework.boot", name: "spring-boot-starter-tomcat", version: spring_boot_version) - testCompile (group: "junit", name: "junit", version: "4.13") + testCompile (group: "junit", name: "junit", version: junit_version) testCompile (group: "org.easytesting", name: "fest-assert", version: "1.4") testCompile (group: "org.springframework.boot", name: "spring-boot-starter-test", version: spring_boot_version) - testCompileOnly (group: "org.projectlombok", name: "lombok", version: "1.18.8") testAnnotationProcessor (group: "org.projectlombok", name: "lombok", version: "1.18.8") } @@ -104,7 +103,8 @@ task convert_cr_lf { processResources { filesMatching("ngrinder-sh/agent/run_agent_internal.*") { - expand(["ngrinder_core": String.format("lib/ngrinder-core-%s.jar", project.version)]) + expand(["ngrinder_core": String.format("lib/ngrinder-core-%s.jar", project.version), + "ngrinder_runtime": String.format("lib/ngrinder-runime-%s.jar", project.version)]) } } @@ -114,4 +114,5 @@ test { tasks.bootWar.dependsOn convert_cr_lf -tasks.processResources.dependsOn tasks.getByPath(":ngrinder-frontend:webpack") +tasks.processResources.finalizedBy tasks.getByPath(":ngrinder-frontend:webpack") +tasks.processResources.finalizedBy tasks.getByPath(":ngrinder-frontend:checkES5") diff --git a/ngrinder-controller/src/main/java/org/ngrinder/agent/controller/MonitorDownloadController.java b/ngrinder-controller/src/main/java/org/ngrinder/agent/controller/MonitorDownloadController.java index a456a9d3ea..bfca01f119 100644 --- a/ngrinder-controller/src/main/java/org/ngrinder/agent/controller/MonitorDownloadController.java +++ b/ngrinder-controller/src/main/java/org/ngrinder/agent/controller/MonitorDownloadController.java @@ -25,7 +25,6 @@ import javax.servlet.http.HttpServletResponse; import java.io.File; -import java.net.URLClassLoader; import lombok.RequiredArgsConstructor; @@ -67,8 +66,8 @@ public void download(@PathVariable String fileName, HttpServletResponse response @GetMapping("") public String download(ModelMap model) { try { - final File monitorPackage = agentPackageService.createPackage(monitorPackageHandler, (URLClassLoader) getClass().getClassLoader(), - "", null, config.getMonitorPort(), ""); + final File monitorPackage = agentPackageService.createPackage(monitorPackageHandler, "", + null, config.getMonitorPort(), ""); model.clear(); return "redirect:/monitor/download/" + monitorPackage.getName(); } catch (Exception e) { diff --git a/ngrinder-controller/src/main/java/org/ngrinder/agent/service/AgentPackageService.java b/ngrinder-controller/src/main/java/org/ngrinder/agent/service/AgentPackageService.java index 936ddf0a88..53dffb98af 100644 --- a/ngrinder-controller/src/main/java/org/ngrinder/agent/service/AgentPackageService.java +++ b/ngrinder-controller/src/main/java/org/ngrinder/agent/service/AgentPackageService.java @@ -11,11 +11,11 @@ import java.io.*; import java.net.URL; -import java.net.URLClassLoader; import java.util.Set; import lombok.RequiredArgsConstructor; +import static net.grinder.util.AbstractGrinderClassPathProcessor.getClassPaths; import static org.apache.commons.lang.StringUtils.isNotEmpty; import static org.ngrinder.common.util.EncodingUtils.decodePathWithUTF8; import static org.ngrinder.common.util.StringUtils.replaceLast; @@ -39,7 +39,7 @@ public class AgentPackageService { * * @return File package. */ - public File createPackage(PackageHandler packageHandler, URLClassLoader classLoader, String regionName, String connectionIP, int port, String owner) { + public File createPackage(PackageHandler packageHandler, String regionName, String connectionIP, int port, String owner) { synchronized (AgentPackageService.class) { File packageFile = packageHandler.getPackageFile(regionName, connectionIP, owner, false); if (packageFile.exists()) { @@ -47,7 +47,7 @@ public File createPackage(PackageHandler packageHandler, URLClassLoader classLoa } FileUtils.deleteQuietly(packageFile); try (TarArchiveOutputStream tarOutputStream = createTarArchiveStream(packageFile)) { - addDependentLibToTarStream(packageHandler, tarOutputStream, classLoader); + addDependentLibToTarStream(packageHandler, tarOutputStream); if (!(packageHandler instanceof AgentPackageHandler) || isNotEmpty(connectionIP)) { packageHandler.addConfigToPackage(tarOutputStream, packageHandler.getConfigParam(regionName, connectionIP, port, owner)); } @@ -77,19 +77,16 @@ public File createAgentPackage() { */ public File createAgentPackage(String region, String connectionIP, int port, String owner) { synchronized (AgentPackageService.class) { - return createPackage(agentPackageHandler, (URLClassLoader) getClass().getClassLoader(), region, connectionIP, port, owner); + return createPackage(agentPackageHandler, region, connectionIP, port, owner); } } - private void addDependentLibToTarStream(PackageHandler packageHandler, TarArchiveOutputStream tarOutputStream, URLClassLoader classLoader) throws IOException { - if (classLoader == null) { - classLoader = (URLClassLoader) getClass().getClassLoader(); - } + private void addDependentLibToTarStream(PackageHandler packageHandler, TarArchiveOutputStream tarOutputStream) throws IOException { packageHandler.addBaseFolderToPackage(tarOutputStream); - Set libs = packageHandler.getPackageDependentLibs(classLoader); + Set libs = packageHandler.getPackageDependentLibs(); packageHandler.copyShellFile(tarOutputStream); - for (URL eachUrl : classLoader.getURLs()) { + for (URL eachUrl : getClassPaths(getClass().getClassLoader())) { File eachClassPath = new File(decodePathWithUTF8(eachUrl.getFile())); if (!isJar(eachClassPath)) { continue; diff --git a/ngrinder-controller/src/main/java/org/ngrinder/common/model/Home.java b/ngrinder-controller/src/main/java/org/ngrinder/common/model/Home.java index 52be895299..a6afd1a336 100644 --- a/ngrinder-controller/src/main/java/org/ngrinder/common/model/Home.java +++ b/ngrinder-controller/src/main/java/org/ngrinder/common/model/Home.java @@ -42,6 +42,8 @@ public class Home { // HOME_PATH + public static final String PATH_SCRIPT_TEMPLATE_DIRECTORY = "/script_template"; + private static final String PATH_PLUGIN = "plugins"; private static final String PATH_SCRIPT = "script"; private static final String PATH_USER_REPO = "repos"; @@ -269,6 +271,10 @@ public File getDistributedFolderName(String id) { return new File(getPerfTestDirectory(), folderName); } + public File getScriptTemplateDirectory() { + return new File(directory, PATH_SCRIPT_TEMPLATE_DIRECTORY); + } + /** * Get the root directory for given {@link PerfTest} id. * diff --git a/ngrinder-controller/src/main/java/org/ngrinder/common/util/FileUtils.java b/ngrinder-controller/src/main/java/org/ngrinder/common/util/FileUtils.java index 22f5534f12..04494c44fa 100644 --- a/ngrinder-controller/src/main/java/org/ngrinder/common/util/FileUtils.java +++ b/ngrinder-controller/src/main/java/org/ngrinder/common/util/FileUtils.java @@ -14,23 +14,22 @@ package org.ngrinder.common.util; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; -import static org.apache.commons.io.IOUtils.copy; +import static org.apache.commons.io.FileUtils.*; /** * Convenient File utilities. * * @since 3.1 */ +@Slf4j public abstract class FileUtils { - private static final Logger LOGGER = LoggerFactory.getLogger(FileUtils.class); /** * Copy the given resource to the given file. @@ -40,11 +39,15 @@ public abstract class FileUtils { * @since 3.2 */ public static void copyResourceToFile(String resourcePath, File file) { - try (InputStream io = new ClassPathResource(resourcePath).getInputStream(); - FileOutputStream fos = new FileOutputStream(file)) { - copy(io, fos); + copyResourceToFile(new ClassPathResource(resourcePath), file); + } + + public static void copyResourceToFile(Resource resource, File file) { + try (InputStream io = resource.getInputStream()) { + copyToFile(io, file); } catch (IOException e) { - LOGGER.error("error while writing {}", resourcePath, e); + log.error("error while writing {}", resource.getFilename(), e); } } + } diff --git a/ngrinder-controller/src/main/java/org/ngrinder/infra/config/Config.java b/ngrinder-controller/src/main/java/org/ngrinder/infra/config/Config.java index 2531dfe38d..d53c558c95 100644 --- a/ngrinder-controller/src/main/java/org/ngrinder/infra/config/Config.java +++ b/ngrinder-controller/src/main/java/org/ngrinder/infra/config/Config.java @@ -23,6 +23,7 @@ import org.ngrinder.common.constant.ControllerConstants; import org.ngrinder.common.constants.InternalConstants; import org.ngrinder.common.exception.ConfigurationException; +import org.ngrinder.common.exception.NGrinderRuntimeException; import org.ngrinder.common.model.Home; import org.ngrinder.common.util.FileWatchdog; import org.ngrinder.common.util.PropertiesKeyMapper; @@ -51,10 +52,13 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static net.grinder.util.NoOp.noOp; -import static org.apache.commons.io.FileUtils.readFileToString; +import static org.apache.commons.io.FileUtils.*; import static org.apache.commons.lang.StringUtils.isEmpty; import static org.ngrinder.common.constant.DatabaseConstants.PROP_DATABASE_UNIT_TEST; import static org.ngrinder.common.constants.GrinderConstants.GRINDER_SECURITY_LEVEL_NORMAL; +import static org.ngrinder.common.model.Home.PATH_SCRIPT_TEMPLATE_DIRECTORY; +import static org.ngrinder.common.util.FileUtils.copyResourceToFile; +import static org.ngrinder.common.util.PathUtils.getSubPath; import static org.ngrinder.common.util.Preconditions.checkNotNull; /** @@ -124,6 +128,7 @@ public void init() { home.init(); exHome = resolveExHome(); copyDefaultConfigurationFiles(); + copyScriptTemplates(); loadInternalProperties(); loadProperties(); initHomeMonitor(); @@ -150,10 +155,14 @@ protected void initDevModeProperties() { } private boolean resolveClusterMode() { - String mode = getClusterProperties().getProperty(PROP_CLUSTER_MODE, "none"); + String mode = getClusterMode(); return !"none".equals(mode) || getClusterProperties().getPropertyBoolean(PROP_CLUSTER_ENABLED); } + public String getClusterMode() { + return getClusterProperties().getProperty(PROP_CLUSTER_MODE, "none"); + } + /** * Destroy bean. */ @@ -258,6 +267,31 @@ protected void copyDefaultConfigurationFiles() throws IOException { home.copyFrom(resources); } + private void copyScriptTemplates() { + File scriptTemplateDirectory = home.getScriptTemplateDirectory(); + if (!scriptTemplateDirectory.exists()) { + try { + ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + Resource[] resources = resolver.getResources("classpath*:script_template/**/*.*"); + for (Resource resource : resources) { + String scriptTemplatePath = home.getScriptTemplateDirectory().getAbsolutePath(); + scriptTemplatePath += getSubPath(PATH_SCRIPT_TEMPLATE_DIRECTORY, resource.getURL().getPath()); + copyResourceToFile(resource, new File(scriptTemplatePath)); + } + } catch (IOException e) { + throw new NGrinderRuntimeException("Error while copying script templates.", e); + } + } + } + + public File getHomeScriptTemplateDirectory() { + File scriptTemplateDirectory = home.getScriptTemplateDirectory(); + if (!scriptTemplateDirectory.exists()) { + copyScriptTemplates(); + } + return scriptTemplateDirectory; + } + /** * Resolve nGrinder home path. * diff --git a/ngrinder-controller/src/main/java/org/ngrinder/infra/config/Database.java b/ngrinder-controller/src/main/java/org/ngrinder/infra/config/Database.java index 2c93a2199b..a862dd1181 100644 --- a/ngrinder-controller/src/main/java/org/ngrinder/infra/config/Database.java +++ b/ngrinder-controller/src/main/java/org/ngrinder/infra/config/Database.java @@ -62,6 +62,7 @@ protected void setupVariants(BasicDataSource dataSource, PropertiesWrapper datab final String databaseURL = databaseProperties.getProperty(DatabaseConfig.PROP_DATABASE_URL); if (databaseURL.startsWith("tcp://")) { format = "jdbc:h2:" + databaseURL; + setClusterSupport(true); } if (databaseProperties.exist(PROP_DATABASE_UNIT_TEST)) { format = format + ";LOCK_MODE=0"; @@ -84,7 +85,7 @@ protected void setupVariants(BasicDataSource dataSource, PropertiesWrapper datab private final String urlTemplate; private final String jdbcDriverName; private final String dialect; - private final boolean clusterSupport; + private boolean clusterSupport; /** * Constructor with cluster mode true. @@ -208,4 +209,8 @@ public String getDialect() { public boolean isClusterSupport() { return clusterSupport; } + + public void setClusterSupport(boolean clusterSupport) { + this.clusterSupport = clusterSupport; + } } diff --git a/ngrinder-controller/src/main/java/org/ngrinder/infra/config/DynamicCacheConfig.java b/ngrinder-controller/src/main/java/org/ngrinder/infra/config/DynamicCacheConfig.java index bd681f7204..c9b04d94b2 100644 --- a/ngrinder-controller/src/main/java/org/ngrinder/infra/config/DynamicCacheConfig.java +++ b/ngrinder-controller/src/main/java/org/ngrinder/infra/config/DynamicCacheConfig.java @@ -22,6 +22,7 @@ import com.hazelcast.spring.context.SpringManagedContext; import com.hazelcast.topic.ITopic; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import net.grinder.util.NetworkUtils; import org.ngrinder.common.constant.ClusterConstants; import org.ngrinder.infra.hazelcast.topic.message.TopicEvent; @@ -39,8 +40,9 @@ import static java.util.concurrent.TimeUnit.SECONDS; import static net.grinder.util.NetworkUtils.DEFAULT_LOCAL_HOST_ADDRESS; -import static net.grinder.util.NetworkUtils.selectLocalIp; +import static org.apache.commons.lang.ArrayUtils.EMPTY_STRING_ARRAY; import static org.ngrinder.common.constant.CacheConstants.*; +import static org.ngrinder.common.util.ObjectUtils.defaultIfNull; import static org.ngrinder.infra.logger.CoreLogger.LOGGER; /** @@ -48,6 +50,7 @@ * * @since 3.1 */ +@Slf4j @Configuration @RequiredArgsConstructor public class DynamicCacheConfig implements ClusterConstants { @@ -95,16 +98,28 @@ public HazelcastInstance hazelcastInstance() { hazelcastConfig.addExecutorConfig(getExecutorConfig(AGENT_EXECUTOR_SERVICE_NAME)); hazelcastConfig.addTopicConfig(getTopicConfig()); NetworkConfig networkConfig = hazelcastConfig.getNetworkConfig(); - networkConfig.setPort(getClusterPort()).setPortAutoIncrement(false); JoinConfig join = networkConfig.getJoin(); join.getMulticastConfig().setEnabled(false); - if (isClustered() && getClusterURIs() != null && getClusterURIs().length > 0) { + if (isClustered()) { TcpIpConfig tcpIpConfig = join.getTcpIpConfig(); tcpIpConfig.setEnabled(true); - tcpIpConfig.setMembers(Arrays.asList(getClusterURIs())); - networkConfig.setPublicAddress(selectLocalIp(Arrays.asList(getClusterURIs()))); + String clusterMode = config.getClusterMode(); + String[] clusterURIs = defaultIfNull(getClusterURIs(), EMPTY_STRING_ARRAY); + + if ("easy".equals(clusterMode)) { + tcpIpConfig.addMember(DEFAULT_LOCAL_HOST_ADDRESS); + } else { + networkConfig.setPort(getClusterPort()).setPortAutoIncrement(false); + if (clusterURIs.length > 0) { + tcpIpConfig.setMembers(Arrays.asList(clusterURIs)); + } else { + log.warn("nGrinder system configuration 'cluster.members' is required in advanced clustering.\n" + + "Please set comma separated IP list of all clustered controller servers in your system configuration."); + } + } + } if (!isClustered()) { diff --git a/ngrinder-controller/src/main/java/org/ngrinder/infra/config/SecurityConfig.java b/ngrinder-controller/src/main/java/org/ngrinder/infra/config/SecurityConfig.java index 11956fb538..430d724aa8 100644 --- a/ngrinder-controller/src/main/java/org/ngrinder/infra/config/SecurityConfig.java +++ b/ngrinder-controller/src/main/java/org/ngrinder/infra/config/SecurityConfig.java @@ -78,6 +78,9 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { private final PluggablePreAuthFilter pluggablePreAuthFilter; + private static final String REMEMBER_ME_KEY = "ngrinder"; + private static final String REMEMBER_ME_COOKIE_NAME = "ngrinder-remember-me"; + @Bean @Override protected AuthenticationManager authenticationManager() throws Exception { @@ -93,7 +96,9 @@ public AbstractAccessDecisionManager accessDecisionManager() { @Bean public TokenBasedRememberMeServices rememberMeServices() { - return new TokenBasedRememberMeServices("ngrinder", ngrinderUserDetailsService); + TokenBasedRememberMeServices tokenBasedRememberMeServices = new TokenBasedRememberMeServices(REMEMBER_ME_KEY, ngrinderUserDetailsService); + tokenBasedRememberMeServices.setCookieName(REMEMBER_ME_COOKIE_NAME); + return tokenBasedRememberMeServices; } /** @@ -161,14 +166,15 @@ protected void configure(HttpSecurity http) throws Exception { .logout() .logoutRequestMatcher(new AntPathRequestMatcher("/logout")) .logoutSuccessUrl("/") - .deleteCookies("JSESSIONID","switchUser") + .deleteCookies("JSESSIONID", "switchUser") .invalidateHttpSession(true) .and() .sessionManagement() .sessionFixation().newSession() .and() .rememberMe() - .key("ngrinder") + .rememberMeCookieName(REMEMBER_ME_COOKIE_NAME) + .key(REMEMBER_ME_KEY) .userDetailsService(ngrinderUserDetailsService) .and() .csrf().disable().exceptionHandling().authenticationEntryPoint(delegatingAuthenticationEntryPoint()) diff --git a/ngrinder-controller/src/main/java/org/ngrinder/infra/config/TaskConfig.java b/ngrinder-controller/src/main/java/org/ngrinder/infra/config/TaskConfig.java index 38aefb0c22..0a0f491389 100644 --- a/ngrinder-controller/src/main/java/org/ngrinder/infra/config/TaskConfig.java +++ b/ngrinder-controller/src/main/java/org/ngrinder/infra/config/TaskConfig.java @@ -35,6 +35,7 @@ public TaskScheduler taskScheduler() { taskScheduler.initialize(); return taskScheduler; } + @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); diff --git a/ngrinder-controller/src/main/java/org/ngrinder/packages/AgentPackageHandler.java b/ngrinder-controller/src/main/java/org/ngrinder/packages/AgentPackageHandler.java index d85c120395..dd558c5db2 100644 --- a/ngrinder-controller/src/main/java/org/ngrinder/packages/AgentPackageHandler.java +++ b/ngrinder-controller/src/main/java/org/ngrinder/packages/AgentPackageHandler.java @@ -6,7 +6,6 @@ import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; -import java.net.URLClassLoader; import java.util.Map; import java.util.Set; @@ -74,8 +73,8 @@ public Map getConfigParam(String regionName, String controllerIP } @Override - public Set getPackageDependentLibs(URLClassLoader urlClassLoader) { - Set libs = getDependentLibs(urlClassLoader); + public Set getPackageDependentLibs() { + Set libs = getDependentLibs(); libs.add("ngrinder-core"); libs.add("ngrinder-runtime"); libs.add("ngrinder-groovy"); diff --git a/ngrinder-controller/src/main/java/org/ngrinder/packages/MonitorPackageHandler.java b/ngrinder-controller/src/main/java/org/ngrinder/packages/MonitorPackageHandler.java index 2802e0afb8..4c14b9fe30 100644 --- a/ngrinder-controller/src/main/java/org/ngrinder/packages/MonitorPackageHandler.java +++ b/ngrinder-controller/src/main/java/org/ngrinder/packages/MonitorPackageHandler.java @@ -2,7 +2,6 @@ import org.springframework.stereotype.Component; -import java.net.URLClassLoader; import java.util.Map; import java.util.Set; @@ -17,8 +16,8 @@ public Map getConfigParam(String regionName, String controllerIP } @Override - public Set getPackageDependentLibs(URLClassLoader urlClassLoader) { - return super.getDependentLibs(urlClassLoader); + public Set getPackageDependentLibs() { + return getDependentLibs(); } @Override diff --git a/ngrinder-controller/src/main/java/org/ngrinder/packages/PackageHandler.java b/ngrinder-controller/src/main/java/org/ngrinder/packages/PackageHandler.java index 3617381269..e8bc7741e8 100644 --- a/ngrinder-controller/src/main/java/org/ngrinder/packages/PackageHandler.java +++ b/ngrinder-controller/src/main/java/org/ngrinder/packages/PackageHandler.java @@ -19,14 +19,13 @@ import org.springframework.core.io.support.ResourcePatternResolver; import java.io.*; -import java.net.URLClassLoader; -import java.nio.charset.Charset; import java.util.HashSet; import java.util.Map; import java.util.Set; import static freemarker.template.Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS; import static java.nio.charset.Charset.defaultCharset; +import static java.util.Objects.requireNonNull; import static org.apache.commons.lang.StringUtils.isNotEmpty; import static org.apache.commons.lang.StringUtils.trimToEmpty; import static org.ngrinder.common.util.CompressionUtils.*; @@ -34,23 +33,23 @@ public abstract class PackageHandler { - protected final int TIME_MILLIS_OF_DAY = 1000 * 60 * 60 * 24; - private final int EXEC = 0x81ed; + private static final Logger LOGGER = LoggerFactory.getLogger(PackageHandler.class); + private static final int EXEC = 0x81ed; + + protected static final int TIME_MILLIS_OF_DAY = 1000 * 60 * 60 * 24; @Autowired private Config config; - private Logger LOGGER = LoggerFactory.getLogger(PackageHandler.class); - - protected Set getDependentLibs(URLClassLoader urlClassLoader) { + protected Set getDependentLibs() { Set libs = new HashSet<>(); - try (InputStream dependencyStream = urlClassLoader.getResourceAsStream(this.getDependenciesFileName())) { - final String dependencies = IOUtils.toString(dependencyStream, defaultCharset()); + try (InputStream dependencyStream = getClass().getClassLoader().getResourceAsStream(getDependenciesFileName())) { + final String dependencies = IOUtils.toString(requireNonNull(dependencyStream), defaultCharset()); for (String each : StringUtils.split(dependencies, ";")) { libs.add(each.trim().replace("-SNAPSHOT", "")); } } catch (Exception e) { - LOGGER.error("Error while loading " + this.getDependenciesFileName(), e); + LOGGER.error("Error while loading " + getDependenciesFileName(), e); } return libs; } @@ -172,7 +171,7 @@ private String getPackageName() { public abstract Map getConfigParam(String regionName, String controllerIP, int port, String owner); - public abstract Set getPackageDependentLibs(URLClassLoader urlClassLoader); + public abstract Set getPackageDependentLibs(); protected abstract String getModuleName(); diff --git a/ngrinder-controller/src/main/java/org/ngrinder/perftest/service/ClusteredPerfTestService.java b/ngrinder-controller/src/main/java/org/ngrinder/perftest/service/ClusteredPerfTestService.java index 7142d88b5b..0b07858258 100644 --- a/ngrinder-controller/src/main/java/org/ngrinder/perftest/service/ClusteredPerfTestService.java +++ b/ngrinder-controller/src/main/java/org/ngrinder/perftest/service/ClusteredPerfTestService.java @@ -50,7 +50,7 @@ public ClusteredPerfTestService(PerfTestRepository perfTestRepository, ConsoleMa public PerfTest getNextRunnablePerfTestPerfTestCandidate() { List readyPerfTests = getPerfTestRepository().findAllByStatusAndRegionOrderByScheduledTimeAsc( Status.READY, getConfig().getRegion()); - List usersFirstPerfTests = filterCurrentlyRunningTestUsersTest(readyPerfTests); - return usersFirstPerfTests.isEmpty() ? null : readyPerfTests.get(0); + List filteredPerfTests = filterCurrentlyRunningTestUsersTest(readyPerfTests); + return filteredPerfTests.isEmpty() ? null : filteredPerfTests.get(0); } } diff --git a/ngrinder-controller/src/main/java/org/ngrinder/perftest/service/ConsoleManager.java b/ngrinder-controller/src/main/java/org/ngrinder/perftest/service/ConsoleManager.java index 573fa186bb..227101a1eb 100644 --- a/ngrinder-controller/src/main/java/org/ngrinder/perftest/service/ConsoleManager.java +++ b/ngrinder-controller/src/main/java/org/ngrinder/perftest/service/ConsoleManager.java @@ -34,7 +34,9 @@ import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.TimeUnit; +import static net.grinder.util.NetworkUtils.DEFAULT_LOCAL_HOST_ADDRESS; import static net.grinder.util.NetworkUtils.getAvailablePorts; +import static org.apache.commons.lang.StringUtils.defaultIfEmpty; import static org.ngrinder.common.constant.ControllerConstants.*; import static org.ngrinder.common.util.ExceptionUtils.processException; import static org.ngrinder.common.util.NoOp.noOp; @@ -70,14 +72,14 @@ public class ConsoleManager { public void init() { int consoleSize = getConsoleSize(); consoleQueue = new ArrayBlockingQueue<>(consoleSize); - final String currentIP = config.getCurrentIP(); + final String currentIP = defaultIfEmpty(config.getCurrentIP(), DEFAULT_LOCAL_HOST_ADDRESS); for (int port : getAvailablePorts(currentIP, consoleSize, getConsolePortBase(), MAX_PORT_NUMBER)) { final ConsoleEntry consoleEntry = new ConsoleEntry(currentIP, port); try { consoleEntry.occupySocket(); consoleQueue.add(consoleEntry); } catch (Exception ex) { - LOG.error("socket binding to {}:{} is failed", currentIP, port); + LOG.error("Socket binding to {}:{} is failed ({})", currentIP, port, ex.getMessage()); } } diff --git a/ngrinder-controller/src/main/java/org/ngrinder/perftest/service/PerfTestRunnable.java b/ngrinder-controller/src/main/java/org/ngrinder/perftest/service/PerfTestRunnable.java index bf56d2880a..cf1c3a1537 100644 --- a/ngrinder-controller/src/main/java/org/ngrinder/perftest/service/PerfTestRunnable.java +++ b/ngrinder-controller/src/main/java/org/ngrinder/perftest/service/PerfTestRunnable.java @@ -469,6 +469,18 @@ protected void doFinish(boolean initial) { if (!initial && consoleManager.getConsoleInUse().isEmpty()) { return; } + + if (initial) { + String message = "Test is canceled by system"; + for (PerfTest each : perfTestService.getAllProgressing()) { + SingleConsole consoleUsingPort = consoleManager.getConsoleUsingPort(each.getPort()); + doCancel(each, consoleUsingPort, message); + LOG.info(format(each, message, each.getId())); + cleanUp(each); + notifyFinish(each, StopReason.CANCEL_BY_SYSTEM); + } + } + doFinish(); } @@ -539,20 +551,23 @@ private boolean isTestFinishCandidate(PerfTest perfTest, SingleConsole singleCon return false; } + public void doCancel(PerfTest perfTest, SingleConsole singleConsoleInUse) { + doCancel(perfTest, singleConsoleInUse, "Stop requested by user"); + } + /** * Cancel the given {@link PerfTest}. * - * @param perfTest {@link PerfTest} to be canceled. + * @param perfTest {@link PerfTest} to be canceled * @param singleConsoleInUse {@link SingleConsole} which is being used for the given - * {@link PerfTest} + * @param message cancel message */ - public void doCancel(PerfTest perfTest, SingleConsole singleConsoleInUse) { + public void doCancel(PerfTest perfTest, SingleConsole singleConsoleInUse, String message) { LOG.info(format(perfTest, "Cancel test.")); singleConsoleInUse.unregisterSampling(); LOG.info(format(perfTest, "Sampling is stopped")); try { - perfTestService.markProgressAndStatusAndFinishTimeAndStatistics(perfTest, CANCELED, - "Stop requested by user"); + perfTestService.markProgressAndStatusAndFinishTimeAndStatistics(perfTest, CANCELED, message); } catch (Exception e) { LOG.error(format(perfTest,"Error while canceling test : {}", e.getMessage())); LOG.debug(format(perfTest, "Details : "), e); diff --git a/ngrinder-controller/src/main/java/org/ngrinder/perftest/service/PerfTestService.java b/ngrinder-controller/src/main/java/org/ngrinder/perftest/service/PerfTestService.java index 89a0c1388e..356f0c2061 100644 --- a/ngrinder-controller/src/main/java/org/ngrinder/perftest/service/PerfTestService.java +++ b/ngrinder-controller/src/main/java/org/ngrinder/perftest/service/PerfTestService.java @@ -426,8 +426,8 @@ public PerfTest getOne(Long testId) { @Transactional public PerfTest getNextRunnablePerfTestPerfTestCandidate() { List readyPerfTests = perfTestRepository.findAllByStatusOrderByScheduledTimeAsc(Status.READY); - List usersFirstPerfTests = filterCurrentlyRunningTestUsersTest(readyPerfTests); - return usersFirstPerfTests.isEmpty() ? null : readyPerfTests.get(0); + List filteredPerfTests = filterCurrentlyRunningTestUsersTest(readyPerfTests); + return filteredPerfTests.isEmpty() ? null : filteredPerfTests.get(0); } /** @@ -458,7 +458,12 @@ protected List filterCurrentlyRunningTestUsersTest(List perf @Override public List getAllTesting() { - return getAll(null, config.getRegion(), Status.getTestingTestStates()); + return getAll(null, config.getRegion(), Status.getTestStatesByCategory(StatusCategory.TESTING)); + } + + @Override + public List getAllProgressing() { + return getAll(null, config.getRegion(), Status.getTestStatesByCategory(StatusCategory.PROGRESSING)); } public List getAllAbnormalTesting() { @@ -611,6 +616,7 @@ public GrinderProperties getGrinderProperties(PerfTest perfTest, ScriptHandler s grinderProperties.setProperty(GRINDER_PROP_USER, perfTest.getCreatedBy().getUserId()); grinderProperties.setProperty(GRINDER_PROP_JVM_USER_LIBRARY_CLASSPATH, geUserLibraryClassPath(perfTest)); grinderProperties.setInt(GRINDER_PROP_IGNORE_SAMPLE_COUNT, getSafe(perfTest.getIgnoreSampleCount())); + grinderProperties.setBoolean(GRINDER_PROP_CONNECTION_RESET, getSafe(perfTest.getConnectionReset())); grinderProperties.setBoolean(GRINDER_PROP_SECURITY, config.isSecurityEnabled()); grinderProperties.setProperty(GRINDER_PROP_SECURITY_LEVEL, config.getSecurityLevel()); // For backward agent compatibility. diff --git a/ngrinder-controller/src/main/java/org/ngrinder/perftest/service/samplinglistener/AgentDieHardListener.java b/ngrinder-controller/src/main/java/org/ngrinder/perftest/service/samplinglistener/AgentDieHardListener.java index f6e364502b..9fb994557e 100644 --- a/ngrinder-controller/src/main/java/org/ngrinder/perftest/service/samplinglistener/AgentDieHardListener.java +++ b/ngrinder-controller/src/main/java/org/ngrinder/perftest/service/samplinglistener/AgentDieHardListener.java @@ -1,4 +1,4 @@ -/* +/* * 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 @@ -9,7 +9,7 @@ * 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. + * limitations under the License. */ package org.ngrinder.perftest.service.samplinglistener; @@ -54,7 +54,7 @@ public AgentDieHardListener(final SingleConsole singleConsole, final PerfTest pe this.perfTestService = perfTestService; this.agentManager = agentManager; this.scheduledTaskService = scheduledTaskService; - this.scheduledTaskService.addFixedDelayedScheduledTask(this, 2); + this.scheduledTaskService.addFixedDelayedScheduledTask(this, 2000); } @Override diff --git a/ngrinder-controller/src/main/java/org/ngrinder/perftest/service/samplinglistener/PerfTestSamplingCollectorListener.java b/ngrinder-controller/src/main/java/org/ngrinder/perftest/service/samplinglistener/PerfTestSamplingCollectorListener.java index 955c4209a3..ca21cd8baf 100644 --- a/ngrinder-controller/src/main/java/org/ngrinder/perftest/service/samplinglistener/PerfTestSamplingCollectorListener.java +++ b/ngrinder-controller/src/main/java/org/ngrinder/perftest/service/samplinglistener/PerfTestSamplingCollectorListener.java @@ -42,7 +42,7 @@ public PerfTestSamplingCollectorListener(final SingleConsole singleConsole, fina final PerfTestService perfTestService, ScheduledTaskService scheduledTaskService) { this.scheduledTaskService = scheduledTaskService; - // Make it separate asyc call to remove the delay on the sampling. + // Make it separate async call to remove the delay on the sampling. this.runnable = () -> perfTestService.saveStatistics(singleConsole, perfTestId); } diff --git a/ngrinder-controller/src/main/java/org/ngrinder/script/handler/GroovyGradleProjectScriptHandler.java b/ngrinder-controller/src/main/java/org/ngrinder/script/handler/GroovyGradleProjectScriptHandler.java index e9108f0a57..35b3e45de7 100644 --- a/ngrinder-controller/src/main/java/org/ngrinder/script/handler/GroovyGradleProjectScriptHandler.java +++ b/ngrinder-controller/src/main/java/org/ngrinder/script/handler/GroovyGradleProjectScriptHandler.java @@ -20,22 +20,25 @@ */ package org.ngrinder.script.handler; +import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; +import org.ngrinder.common.exception.NGrinderRuntimeException; import org.ngrinder.common.util.PathUtils; import org.ngrinder.infra.config.Config; import org.ngrinder.model.User; import org.ngrinder.script.model.FileEntry; -import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Component; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.List; import static java.lang.System.getenv; import static java.nio.charset.StandardCharsets.UTF_8; import static java.nio.file.Paths.get; +import static java.util.Collections.singletonList; import static org.apache.commons.io.FileUtils.deleteQuietly; import static org.apache.commons.io.FilenameUtils.normalize; import static org.apache.commons.lang.StringUtils.EMPTY; @@ -56,6 +59,7 @@ public class GroovyGradleProjectScriptHandler extends GroovyProjectScriptHandler { public static final String GRADLE_HOME_ENV_NAME = "GRADLE_HOME"; + private static final String PATH_SCRIPT_TEMPLATE_DIRECTORY = "/groovy_gradle"; private final String ngrinderHomePath; private String gradlePath; @@ -92,10 +96,16 @@ public boolean prepareScriptEnv(User user, String path, String fileName, String path = PathUtils.join(path, fileName); // Create Dir entry createBaseDirectory(user, path); + // Create each template entries - createFileEntries(user, path, name, url, scriptContent); - if (createLib) { - createLibraryDirectory(user, path); + try { + createFileEntries(user, path, name, url, scriptContent); + if (createLib) { + createLibraryDirectory(user, path); + } + } catch (NGrinderRuntimeException e) { + getFileEntryRepository().delete(user, new ArrayList<>(singletonList(path))); + throw e; } return false; } @@ -108,8 +118,9 @@ protected void deleteUnnecessaryFilesFromDist(File distDir) { private void createFileEntries(User user, String path, String name, String url, String scriptContent) { String[] scriptTemplatePaths = { getBuildScriptName(), "src/main/resources/resource1.txt", "src/main/java/TestRunner.groovy" }; + String homeScriptTemplateDirectoryPath = getConfig().getHomeScriptTemplateDirectory().getAbsolutePath() + PATH_SCRIPT_TEMPLATE_DIRECTORY; for (String scriptTemplatePath : scriptTemplatePaths) { - try (InputStream inputStream = new ClassPathResource("/script_template/groovy_gradle/" + scriptTemplatePath).getInputStream()) { + try (InputStream inputStream = FileUtils.openInputStream(new File(homeScriptTemplateDirectoryPath + "/" + scriptTemplatePath))) { String fileContent = IOUtils.toString(inputStream, UTF_8.name()); if (scriptTemplatePath.endsWith("TestRunner.groovy")) { fileContent = scriptContent; diff --git a/ngrinder-controller/src/main/java/org/ngrinder/script/handler/ScriptHandler.java b/ngrinder-controller/src/main/java/org/ngrinder/script/handler/ScriptHandler.java index 1c7d8e00e1..1c72f02fbd 100644 --- a/ngrinder-controller/src/main/java/org/ngrinder/script/handler/ScriptHandler.java +++ b/ngrinder-controller/src/main/java/org/ngrinder/script/handler/ScriptHandler.java @@ -18,6 +18,7 @@ import freemarker.template.Configuration; import freemarker.template.DefaultObjectWrapper; import freemarker.template.Template; +import freemarker.template.TemplateNotFoundException; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FilenameUtils; @@ -27,6 +28,7 @@ import org.ngrinder.common.exception.PerfTestPrepareException; import org.ngrinder.common.util.PathUtils; import org.ngrinder.common.util.PropertiesWrapper; +import org.ngrinder.infra.config.Config; import org.ngrinder.model.PerfTest; import org.ngrinder.model.User; import org.ngrinder.script.model.FileEntry; @@ -90,6 +92,10 @@ public ScriptHandler(String key, String extension, String title, String codeMirr @JsonIgnore private GitHubFileEntryRepository gitHubFileEntryRepository; + @Autowired + @JsonIgnore + private Config config; + /** * Get the display order of {@link ScriptHandler}s. * @@ -260,9 +266,9 @@ public List getLibAndResourceEntries(User user, FileEntry scriptEntry } for (FileEntry eachFileEntry : libFileEntries) { - // Skip jython 2.5... it's already included. - if (startsWithIgnoreCase(eachFileEntry.getFileName(), "jython-2.5.") - || startsWithIgnoreCase(eachFileEntry.getFileName(), "jython-standalone-2.5.")) { + // Skip jython 2.7... it's already included. + if (startsWithIgnoreCase(eachFileEntry.getFileName(), "jython-2.7.") + || startsWithIgnoreCase(eachFileEntry.getFileName(), "jython-standalone-2.7.")) { continue; } FileType fileType = eachFileEntry.getFileType(); @@ -327,11 +333,12 @@ public String getScriptExecutePath(String svnPath) { * @return generated string */ public String getScriptTemplate(Map values) { + String templateFileName = "basic_template_" + getExtension() + ".ftl"; try { Configuration freemarkerConfig = new Configuration(DEFAULT_INCOMPATIBLE_IMPROVEMENTS); freemarkerConfig.setObjectWrapper(new DefaultObjectWrapper(DEFAULT_INCOMPATIBLE_IMPROVEMENTS)); - freemarkerConfig.setClassForTemplateLoading(this.getClass() , "/script_template"); - Template template = freemarkerConfig.getTemplate("basic_template_" + getExtension() + ".ftl"); + freemarkerConfig.setDirectoryForTemplateLoading(config.getHomeScriptTemplateDirectory()); + Template template = freemarkerConfig.getTemplate(templateFileName); StringWriter writer = new StringWriter(); template.process(values, writer); return writer.toString(); diff --git a/ngrinder-controller/src/main/java/org/ngrinder/script/service/FileEntryService.java b/ngrinder-controller/src/main/java/org/ngrinder/script/service/FileEntryService.java index 816d04a9a8..3e4bc9d218 100644 --- a/ngrinder-controller/src/main/java/org/ngrinder/script/service/FileEntryService.java +++ b/ngrinder-controller/src/main/java/org/ngrinder/script/service/FileEntryService.java @@ -41,11 +41,10 @@ import java.io.File; import java.net.MalformedURLException; import java.net.URL; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import static com.google.common.collect.Lists.newArrayList; +import static java.util.Collections.singletonList; import static java.util.Collections.unmodifiableList; import static org.apache.commons.compress.utils.CharsetNames.UTF_8; import static org.ngrinder.common.constant.CacheConstants.DIST_CACHE_FILE_ENTRIES; @@ -273,7 +272,7 @@ public void delete(User user, List fullPathFiles) { * @param path the path */ public void delete(User user, String path) { - fileEntityRepository.delete(user, newArrayList(path)); + fileEntityRepository.delete(user, new ArrayList<>(singletonList(path))); } String getPathFromUrl(String urlString) { diff --git a/ngrinder-controller/src/main/java/org/ngrinder/starter/NGrinderControllerStarter.java b/ngrinder-controller/src/main/java/org/ngrinder/starter/NGrinderControllerStarter.java index b50a939444..a8998762b4 100644 --- a/ngrinder-controller/src/main/java/org/ngrinder/starter/NGrinderControllerStarter.java +++ b/ngrinder-controller/src/main/java/org/ngrinder/starter/NGrinderControllerStarter.java @@ -68,9 +68,8 @@ public void process() { "it can only communicate with the other cluster members in the same machine.") private String clusterHost = null; - @Parameter(names = {"-clp", "--cluster-port"}, required = true, - description = "This cluster member's cluster communication port. Each cluster member should " + - "be run with unique cluster port.", + @Parameter(names = {"-clp", "--cluster-port"}, + description = "Deprecated from 3.5.4, the cluster communication port will be resolved automatically in easy clustering", validateValueWith = PortAvailabilityValidator.class) private Integer clusterPort = null; @@ -104,7 +103,6 @@ public void process() { if (clusterHost != null) { System.setProperty("cluster.host", clusterHost); } - System.setProperty("cluster.port", clusterPort.toString()); System.setProperty("cluster.region", region); System.setProperty("controller.controller_port", controllerPort.toString()); System.setProperty("database.type", databaseType); @@ -117,7 +115,7 @@ public void process() { + ".\nPlease run the h2 TcpServer in advance\n" + "or set the correct -database-host and -database-port parameters"); } - System.setProperty("database.url", "tcp://" + this.databaseHost + ":" + databasePort + "/db/ngrinder"); + System.setProperty("database.url", "tcp://" + databaseHost + ":" + databasePort + "/~/db/ngrinder"); } else { if (databasePort == null) { databasePort = 3306; diff --git a/ngrinder-controller/src/main/resources/application.yml b/ngrinder-controller/src/main/resources/application.yml index af6601cf10..7b1cf5a247 100644 --- a/ngrinder-controller/src/main/resources/application.yml +++ b/ngrinder-controller/src/main/resources/application.yml @@ -1,5 +1,5 @@ ngrinder: - version: 3.5.3 + version: 3.5.4 server: default-encoding: UTF-8 diff --git a/ngrinder-controller/src/main/resources/logback/logback-worker.xml b/ngrinder-controller/src/main/resources/logback/logback-worker.xml index 4140169b15..62e45f4c84 100644 --- a/ngrinder-controller/src/main/resources/logback/logback-worker.xml +++ b/ngrinder-controller/src/main/resources/logback/logback-worker.xml @@ -32,4 +32,8 @@ + + + + diff --git a/ngrinder-controller/src/main/resources/messages_cn.properties b/ngrinder-controller/src/main/resources/messages_cn.properties index cb4d9a4ae2..fa48a47979 100644 --- a/ngrinder-controller/src/main/resources/messages_cn.properties +++ b/ngrinder-controller/src/main/resources/messages_cn.properties @@ -205,6 +205,7 @@ perfTest.config.process=\u8FDB\u7A0B\u6570 perfTest.config.thread=\u7EBF\u7A0B\u6570 perfTest.config.ignoreTooManyError=ๅฟฝ็•ฅ้”™่ฏฏ perfTest.config.ignoreTooManyError.help=้”™่ฏฏๅ‘็”Ÿๆ—ถ็ปง็ปญ่ฟ่กŒๆต‹่ฏ•๏ผŒ่ฏท้€‰ๆ‹ฉ่ฏฅ้€‰้กนใ€‚ +perfTest.config.connectionReset=Connection reset on each test run perfTest.report.tab=\u6D4B\u8BD5\u62A5\u544A perfTest.report.detailedReport=\u8BE6\u7EC6\u6D4B\u8BD5\u7ED3\u679C diff --git a/ngrinder-controller/src/main/resources/messages_en.properties b/ngrinder-controller/src/main/resources/messages_en.properties index 3e5a0b56de..f6b6d062c8 100644 --- a/ngrinder-controller/src/main/resources/messages_en.properties +++ b/ngrinder-controller/src/main/resources/messages_en.properties @@ -207,6 +207,7 @@ perfTest.config.process=Processes perfTest.config.thread=Threads perfTest.config.ignoreTooManyError=Ignore Errors perfTest.config.ignoreTooManyError.help=If you want to continue testing even if too many errors occur. Please check this. +perfTest.config.connectionReset=Connection reset on each test run perfTest.report.tab=Report perfTest.report.detailedReport=Detailed Report diff --git a/ngrinder-controller/src/main/resources/messages_kr.properties b/ngrinder-controller/src/main/resources/messages_kr.properties index 3fedbda25c..9183d8f52a 100644 --- a/ngrinder-controller/src/main/resources/messages_kr.properties +++ b/ngrinder-controller/src/main/resources/messages_kr.properties @@ -204,6 +204,7 @@ perfTest.config.process=\uD504\uB85C\uC138\uC2A4 perfTest.config.thread=\uC4F0\uB808\uB4DC perfTest.config.ignoreTooManyError=\uC5D0\uB7EC \uBB34\uC2DC perfTest.config.ignoreTooManyError.help=\uD14C\uC2A4\uD2B8\uB97C \uC5D0\uB7EC \uBC1C\uC0DD\uC5D0 \uB530\uB978 \uC911\uC9C0 \uC5C6\uC774 \uACC4\uC18D \uC9C4\uD589\uD558\uACE0 \uC2F6\uC73C\uC2DC\uBA74 \uC5EC\uAE30\uB97C \uD074\uB9AD\uD558\uC138\uC694. +perfTest.config.connectionReset=ํ…Œ์ŠคํŠธ ์ˆ˜ํ–‰๊ฐ„์— ์—ฐ๊ฒฐ ์žฌ์„ค์ • perfTest.report.tab=\uBCF4\uACE0\uC11C perfTest.report.detailedReport=\uC0C1\uC138 \uBCF4\uACE0\uC11C diff --git a/ngrinder-controller/src/main/resources/ngrinder-sh/agent/run_agent_internal.bat b/ngrinder-controller/src/main/resources/ngrinder-sh/agent/run_agent_internal.bat index 56a3bf6b1c..7a24fdc028 100644 --- a/ngrinder-controller/src/main/resources/ngrinder-sh/agent/run_agent_internal.bat +++ b/ngrinder-controller/src/main/resources/ngrinder-sh/agent/run_agent_internal.bat @@ -4,4 +4,4 @@ for /R ./lib %%a in (*.jar) do ( set CLASSPATH=!CLASSPATH!;%%a ) set CLASSPATH=!CLASSPATH! -java -server -cp "${ngrinder_core};%CLASSPATH%" org.ngrinder.NGrinderAgentStarter --mode agent --command run %* +java -server -cp "${ngrinder_core};${ngrinder_runtime};%CLASSPATH%" org.ngrinder.NGrinderAgentStarter --mode agent --command run %* diff --git a/ngrinder-controller/src/main/resources/ngrinder-sh/agent/run_agent_internal.sh b/ngrinder-controller/src/main/resources/ngrinder-sh/agent/run_agent_internal.sh index 97b9144e60..d131806ddf 100644 --- a/ngrinder-controller/src/main/resources/ngrinder-sh/agent/run_agent_internal.sh +++ b/ngrinder-controller/src/main/resources/ngrinder-sh/agent/run_agent_internal.sh @@ -1,2 +1,2 @@ #!/bin/sh -java -server -cp "${ngrinder_core}:lib/*" org.ngrinder.NGrinderAgentStarter --mode=agent --command=run \$@ +java -server -cp "${ngrinder_core}:${ngrinder_runtime}:lib/*" org.ngrinder.NGrinderAgentStarter --mode=agent --command=run \$@ diff --git a/ngrinder-controller/src/main/resources/ngrinder_datachange_logfile/db.changelog.xml b/ngrinder-controller/src/main/resources/ngrinder_datachange_logfile/db.changelog.xml index 919829ade2..674cd208ea 100644 --- a/ngrinder-controller/src/main/resources/ngrinder_datachange_logfile/db.changelog.xml +++ b/ngrinder-controller/src/main/resources/ngrinder_datachange_logfile/db.changelog.xml @@ -41,4 +41,5 @@ + diff --git a/ngrinder-controller/src/main/resources/ngrinder_datachange_logfile/db.changelog_schema_32.xml b/ngrinder-controller/src/main/resources/ngrinder_datachange_logfile/db.changelog_schema_32.xml new file mode 100644 index 0000000000..dfe36fb4bf --- /dev/null +++ b/ngrinder-controller/src/main/resources/ngrinder_datachange_logfile/db.changelog_schema_32.xml @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/ngrinder-controller/src/main/resources/script_template/basic_template_groovy.ftl b/ngrinder-controller/src/main/resources/script_template/basic_template_groovy.ftl index e597e0a339..e78600adcb 100644 --- a/ngrinder-controller/src/main/resources/script_template/basic_template_groovy.ftl +++ b/ngrinder-controller/src/main/resources/script_template/basic_template_groovy.ftl @@ -1,8 +1,6 @@ import static net.grinder.script.Grinder.grinder import static org.junit.Assert.* import static org.hamcrest.Matchers.* -import net.grinder.plugin.http.HTTPRequest -import net.grinder.plugin.http.HTTPPluginControl import net.grinder.script.GTest import net.grinder.script.Grinder import net.grinder.scriptengine.groovy.junit.GrinderRunner @@ -18,17 +16,26 @@ import java.util.Date import java.util.List import java.util.ArrayList +import net.grinder.plugin.http.HTTPRequest +import net.grinder.plugin.http.HTTPPluginControl + import HTTPClient.Cookie import HTTPClient.CookieModule import HTTPClient.HTTPResponse import HTTPClient.NVPair +// Uncomment this to use new experimental HTTP client. +// import org.ngrinder.http.HTTPRequest +// import org.ngrinder.http.HTTPResponse +// import org.ngrinder.http.cookie.Cookie +// import org.ngrinder.http.cookie.CookieManager + /** * A simple example using the HTTP plugin that shows the retrieval of a - * single page via HTTP. - * + * single page via HTTP. + * * This script is automatically generated by ngrinder. - * + * * @author ${userName} */ @RunWith(GrinderRunner) @@ -49,7 +56,7 @@ class TestRunner { public static HTTPRequest request public static NVPair[] headers = [] <#if body??> - public static String body = "${body?j_string?replace("$", "\\$")}" + public static String body = "${body?j_string?replace("$", "\\$")}" <#else> public static NVPair[] params = [] @@ -87,13 +94,13 @@ class TestRunner { grinder.logger.info("before process."); } - @BeforeThread + @BeforeThread public void beforeThread() { test.record(this, "test") grinder.statistics.delayReports=true; grinder.logger.info("before thread."); } - + @Before public void before() { request.setHeaders(headers) @@ -106,7 +113,7 @@ class TestRunner { HTTPResponse result = request.${method?default("GET")}("${url}", <#if body??>body.getBytes()<#else>params) if (result.statusCode == 301 || result.statusCode == 302) { - grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode); + grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode); } else { assertThat(result.statusCode, is(200)); } diff --git a/ngrinder-controller/src/main/resources/script_template/basic_template_py.ftl b/ngrinder-controller/src/main/resources/script_template/basic_template_py.ftl index 993dc4d17d..dfe823504b 100644 --- a/ngrinder-controller/src/main/resources/script_template/basic_template_py.ftl +++ b/ngrinder-controller/src/main/resources/script_template/basic_template_py.ftl @@ -1,18 +1,23 @@ # -*- coding:utf-8 -*- # A simple example using the HTTP plugin that shows the retrieval of a -# single page via HTTP. +# single page via HTTP. # # This script is automatically generated by ngrinder. # # @author ${userName} from net.grinder.script.Grinder import grinder from net.grinder.script import Test -from net.grinder.plugin.http import HTTPRequest -from net.grinder.plugin.http import HTTPPluginControl from java.util import Date from HTTPClient import NVPair, Cookie, CookieModule +from net.grinder.plugin.http import HTTPRequest +from net.grinder.plugin.http import HTTPPluginControl + +# Uncomment this to use new experimental HTTP client. +# from org.ngrinder.http import HTTPRequest +# from org.ngrinder.http.cookie import Cookie, CookieManager + control = HTTPPluginControl.getConnectionDefaults() # if you don't want that HTTPRequest follows the redirection, please modify the following option 0. # control.followRedirects = 1 @@ -61,33 +66,33 @@ cookies.append(Cookie("${cookie["name"]?j_string}", "${cookie["value"]?j_string} class TestRunner: - # initlialize a thread + # initlialize a thread def __init__(self): test1.record(TestRunner.__call__) grinder.statistics.delayReports=True pass - + def before(self): - request1.headers = headers + request1.setHeaders(headers) for c in cookies: CookieModule.addCookie(c, HTTPPluginControl.getThreadHTTPClientContext()) - # test method + # test method def __call__(self): self.before() - + result = request1.${method?default("GET")}("${url}", <#if body??>body<#else>params) - + # You get the message body using the getText() method. # if result.getText().find("HELLO WORLD") == -1 : # raise - + # if you want to print out log.. Don't use print keyword. Instead, use following. # grinder.logger.info("Hello World") - + if result.getStatusCode() == 200 : return elif result.getStatusCode() in (301, 302) : - grinder.logger.warn("Warning. The response may not be correct. The response code was %d." % result.getStatusCode()) + grinder.logger.warn("Warning. The response may not be correct. The response code was %d." % result.getStatusCode()) return else : raise diff --git a/ngrinder-controller/src/main/resources/script_template/groovy_gradle/build.gradle b/ngrinder-controller/src/main/resources/script_template/groovy_gradle/build.gradle index 5e7222f5de..6a79f3296a 100644 --- a/ngrinder-controller/src/main/resources/script_template/groovy_gradle/build.gradle +++ b/ngrinder-controller/src/main/resources/script_template/groovy_gradle/build.gradle @@ -15,7 +15,7 @@ repositories { } ext { - ngrinder_version = "3.5.3" + ngrinder_version = "3.5.4" } dependencies { diff --git a/ngrinder-controller/src/main/resources/script_template/groovy_gradle/src/main/java/TestRunner.groovy b/ngrinder-controller/src/main/resources/script_template/groovy_gradle/src/main/java/TestRunner.groovy index e69de29bb2..d6f30af278 100644 --- a/ngrinder-controller/src/main/resources/script_template/groovy_gradle/src/main/java/TestRunner.groovy +++ b/ngrinder-controller/src/main/resources/script_template/groovy_gradle/src/main/java/TestRunner.groovy @@ -0,0 +1,2 @@ +// Empty file +// This template will be filled out with the contents of 'script_template_groovy.ftl' diff --git a/ngrinder-controller/src/test/java/org/ngrinder/script/service/FileEntryServiceTest.java b/ngrinder-controller/src/test/java/org/ngrinder/script/service/FileEntryServiceTest.java index 3dcb7522a0..5fe92c5adf 100644 --- a/ngrinder-controller/src/test/java/org/ngrinder/script/service/FileEntryServiceTest.java +++ b/ngrinder-controller/src/test/java/org/ngrinder/script/service/FileEntryServiceTest.java @@ -18,19 +18,25 @@ import static org.hamcrest.MatcherAssert.assertThat; import org.junit.Test; +import org.ngrinder.AbstractNGrinderTransactionalTest; import org.ngrinder.common.exception.NGrinderRuntimeException; import org.ngrinder.model.User; -import org.ngrinder.script.handler.JythonScriptHandler; +import org.ngrinder.script.handler.ScriptHandlerFactory; +import org.springframework.beans.factory.annotation.Autowired; -public class FileEntryServiceTest { +public class FileEntryServiceTest extends AbstractNGrinderTransactionalTest { - private FileEntryService fileEntryService = new FileEntryService(null, null, null, null); + @Autowired + private FileEntryService fileEntryService; + + @Autowired + private ScriptHandlerFactory scriptHandlerFactory; @Test public void testFileTemplateWithoutOptions() { User user = new User(); user.setUserName("JunHo Yoon"); - String content = fileEntryService.loadTemplate(user, new JythonScriptHandler(), "http://helloworld/myname/is", + String content = fileEntryService.loadTemplate(user, scriptHandlerFactory.getHandler("jython"), "http://helloworld/myname/is", "hello", null); assertThat(content, containsString("JunHo Yoon")); assertThat(content, containsString("http://helloworld/myname/is")); @@ -45,7 +51,7 @@ public void testFileTemplateWithOptions() { "\"cookies\":[{\"name\":\"cook\",\"value\":\"good\",\"domain\":\"naver.com\",\"path\":\"/home\"}]}"; User user = new User(); user.setUserName("Gisoo Gwon"); - String content = fileEntryService.loadTemplate(user, new JythonScriptHandler(), "http://helloworld/myname/is", + String content = fileEntryService.loadTemplate(user, scriptHandlerFactory.getHandler("jython"), "http://helloworld/myname/is", "hello", options); System.out.println(content); assertThat(content, containsString("Gisoo Gwon")); diff --git a/ngrinder-core/build.gradle b/ngrinder-core/build.gradle index 24b0bd8044..2d99357291 100644 --- a/ngrinder-core/build.gradle +++ b/ngrinder-core/build.gradle @@ -8,7 +8,6 @@ dependencies { compile (group: "org.pf4j", name: "pf4j", version:"3.0.1") compile (group: "javax.servlet", name: "javax.servlet-api", version:"3.1.0") compile (group: "commons-collections", name: "commons-collections", version:"3.2.1") - compile (group: "org.python", name: "jython-standalone", version:"2.5.3") compile (group: "com.fasterxml.jackson.core", name: "jackson-annotations", version: jackson_version) compile (group: "com.fasterxml.jackson.core", name: "jackson-databind", version: jackson_version) compile (group: "net.java.dev.jna", name: "jna", version:"5.6.0") diff --git a/ngrinder-core/src/main/java/net/grinder/AgentController.java b/ngrinder-core/src/main/java/net/grinder/AgentController.java index 97d40f6ec0..5147d54175 100644 --- a/ngrinder-core/src/main/java/net/grinder/AgentController.java +++ b/ngrinder-core/src/main/java/net/grinder/AgentController.java @@ -46,6 +46,7 @@ import java.util.Timer; import java.util.TimerTask; +import static net.grinder.util.NetworkUtils.getIP; import static org.ngrinder.common.constants.InternalConstants.PROP_INTERNAL_NGRINDER_VERSION; import static org.ngrinder.common.util.NoOp.noOp; import static org.ngrinder.common.util.Preconditions.checkNotNull; @@ -125,7 +126,9 @@ public void run() { connector = m_connectorFactory.create(agentConfig.getConnectionAgentPort()); occupyConnectionAgentSocket(); } else { - connector = m_connectorFactory.create(agentConfig.getControllerIP(), agentConfig.getControllerPort()); + String controllerIP = getIP(agentConfig.getControllerHost()); + agentConfig.setControllerIP(controllerIP); + connector = m_connectorFactory.create(controllerIP, agentConfig.getControllerPort()); } try { diff --git a/ngrinder-core/src/main/java/net/grinder/SingleConsole.java b/ngrinder-core/src/main/java/net/grinder/SingleConsole.java index 77f3b702db..e3c5b1d049 100644 --- a/ngrinder-core/src/main/java/net/grinder/SingleConsole.java +++ b/ngrinder-core/src/main/java/net/grinder/SingleConsole.java @@ -1051,7 +1051,7 @@ private void writeReportData(String name, String value) { bw.newLine(); bw.flush(); } catch (Exception e) { - LOGGER.error(e.getMessage(), e); + LOGGER.error("Error while writing report data to {}", name , e); throw processException(e); } } diff --git a/ngrinder-core/src/main/java/net/grinder/StopReason.java b/ngrinder-core/src/main/java/net/grinder/StopReason.java index a2e048b010..f88e0f5879 100644 --- a/ngrinder-core/src/main/java/net/grinder/StopReason.java +++ b/ngrinder-core/src/main/java/net/grinder/StopReason.java @@ -33,7 +33,9 @@ public enum StopReason { /** Normal Stop. */ NORMAL("Normal stop"), /** Cancel By User. */ - CANCEL_BY_USER("Cancel by user"); + CANCEL_BY_USER("Cancel by user"), + /** Cancel By System. */ + CANCEL_BY_SYSTEM("Cancel by system"); private final String display; diff --git a/ngrinder-core/src/main/java/net/grinder/engine/agent/AgentImplementationEx.java b/ngrinder-core/src/main/java/net/grinder/engine/agent/AgentImplementationEx.java index 0bc79c7a10..1594c5ca89 100644 --- a/ngrinder-core/src/main/java/net/grinder/engine/agent/AgentImplementationEx.java +++ b/ngrinder-core/src/main/java/net/grinder/engine/agent/AgentImplementationEx.java @@ -27,7 +27,6 @@ import net.grinder.common.GrinderProperties.PersistenceException; import net.grinder.common.processidentity.ProcessReport; import net.grinder.communication.*; -import net.grinder.console.ConsoleFoundation; import net.grinder.engine.common.ConnectorFactory; import net.grinder.engine.common.EngineException; import net.grinder.engine.common.ScriptLocation; diff --git a/ngrinder-core/src/main/java/net/grinder/engine/agent/LocalScriptTestDriveService.java b/ngrinder-core/src/main/java/net/grinder/engine/agent/LocalScriptTestDriveService.java index 2fe87369d6..8f4f0096e8 100644 --- a/ngrinder-core/src/main/java/net/grinder/engine/agent/LocalScriptTestDriveService.java +++ b/ngrinder-core/src/main/java/net/grinder/engine/agent/LocalScriptTestDriveService.java @@ -31,9 +31,9 @@ import java.io.*; import java.net.URL; -import java.net.URLClassLoader; import java.util.Properties; +import static net.grinder.util.AbstractGrinderClassPathProcessor.getClassPaths; import static org.ngrinder.common.constants.GrinderConstants.GRINDER_PROP_JVM_CLASSPATH; import static org.ngrinder.common.util.EncodingUtils.decodePathWithUTF8; import static org.ngrinder.common.util.NoOp.noOp; @@ -232,12 +232,12 @@ private String getHomeLibraryPath(String classPath) { } private boolean isRunningOnWas() { - return ((URLClassLoader) LocalScriptTestDriveService.class.getClassLoader()).getURLs()[0].getProtocol().equals("jar"); + return getClassPaths(LocalScriptTestDriveService.class.getClassLoader())[0].getProtocol().equals("jar"); } private String runtimeClassPath() { StringBuilder runtimeClassPath = new StringBuilder(); - for (URL url : ((URLClassLoader) LocalScriptTestDriveService.class.getClassLoader()).getURLs()) { + for (URL url : getClassPaths(LocalScriptTestDriveService.class.getClassLoader())) { if (url.getPath().contains("ngrinder-runtime") || url.getPath().contains("ngrinder-groovy")) { runtimeClassPath.append(decodePathWithUTF8(url.getFile())).append(File.pathSeparator); } diff --git a/ngrinder-core/src/main/java/net/grinder/engine/agent/PropertyBuilder.java b/ngrinder-core/src/main/java/net/grinder/engine/agent/PropertyBuilder.java index aeb33ea0a5..7271b6f5e0 100644 --- a/ngrinder-core/src/main/java/net/grinder/engine/agent/PropertyBuilder.java +++ b/ngrinder-core/src/main/java/net/grinder/engine/agent/PropertyBuilder.java @@ -34,6 +34,7 @@ import static java.util.Collections.singletonList; import static javax.net.ssl.SSLSocketFactory.getDefault; +import static org.ngrinder.common.constants.GrinderConstants.GRINDER_PROP_CONNECTION_RESET; import static org.ngrinder.common.constants.GrinderConstants.GRINDER_SECURITY_LEVEL_LIGHT; import static org.ngrinder.common.util.NoOp.noOp; import static org.ngrinder.common.util.Preconditions.checkNotEmpty; @@ -184,6 +185,10 @@ public String buildJVMArgumentWithoutMemory() { addNativeLibraryPath(jvmArguments); } + if (properties.getBoolean(GRINDER_PROP_CONNECTION_RESET, false)) { + jvmArguments.append(" -Dngrinder.connection.reset.on.each.test.run=true "); + } + addParam(jvmArguments, properties.getProperty("grinder.param", "")); addPythonPathJvmArgument(jvmArguments); addCustomDns(jvmArguments); diff --git a/ngrinder-core/src/main/java/net/grinder/lang/jython/JythonGrinderClassPathProcessor.java b/ngrinder-core/src/main/java/net/grinder/lang/jython/JythonGrinderClassPathProcessor.java index e81b96f4b6..cea95ed434 100644 --- a/ngrinder-core/src/main/java/net/grinder/lang/jython/JythonGrinderClassPathProcessor.java +++ b/ngrinder-core/src/main/java/net/grinder/lang/jython/JythonGrinderClassPathProcessor.java @@ -35,8 +35,8 @@ public JythonGrinderClassPathProcessor() { @Override protected void initMore() { List usefulJarList = getUsefulJarList(); - usefulJarList.add("jython-2.5"); - usefulJarList.add("jython-standalone-2.5"); + usefulJarList.add("jython-2.7"); + usefulJarList.add("jython-standalone-2.7"); usefulJarList.add("commons-io"); usefulJarList.add("commons-lang"); } diff --git a/ngrinder-core/src/main/java/net/grinder/util/AbstractGrinderClassPathProcessor.java b/ngrinder-core/src/main/java/net/grinder/util/AbstractGrinderClassPathProcessor.java index 583e069b30..0a5c61e9a6 100644 --- a/ngrinder-core/src/main/java/net/grinder/util/AbstractGrinderClassPathProcessor.java +++ b/ngrinder-core/src/main/java/net/grinder/util/AbstractGrinderClassPathProcessor.java @@ -18,10 +18,13 @@ import org.slf4j.Logger; import java.io.File; +import java.lang.management.ManagementFactory; +import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.List; +import java.util.stream.Stream; import static org.ngrinder.common.util.CollectionUtils.newArrayList; import static org.ngrinder.common.util.EncodingUtils.decodePathWithUTF8; @@ -157,6 +160,8 @@ public void init() { usefulJarList.add("junit"); usefulJarList.add("hamcrest"); usefulJarList.add("commons-lang"); + usefulJarList.add("httpcore5"); + usefulJarList.add("httpcore5-h2"); uselessJarList.add("jython-2.2"); uselessJarList.add("ngrinder-core"); @@ -209,9 +214,9 @@ private boolean isUsefulJar(String jarFilename) { * @return classpath optimized for grinder. */ public String buildForemostClasspathBasedOnCurrentClassLoader(Logger logger) { - URL[] urLs = ((URLClassLoader) AbstractGrinderClassPathProcessor.class.getClassLoader()).getURLs(); + URL[] urls = getClassPaths(AbstractGrinderClassPathProcessor.class.getClassLoader()); StringBuilder builder = new StringBuilder(); - for (URL each : urLs) { + for (URL each : urls) { builder.append(decodePathWithUTF8(each.getFile())).append(File.pathSeparator); } return filterForeMostClassPath(builder.toString(), logger); @@ -224,14 +229,33 @@ public String buildForemostClasspathBasedOnCurrentClassLoader(Logger logger) { * @return classpath optimized for grinder. */ public String buildPatchClasspathBasedOnCurrentClassLoader(Logger logger) { - URL[] urLs = ((URLClassLoader) AbstractGrinderClassPathProcessor.class.getClassLoader()).getURLs(); + URL[] urls = getClassPaths(AbstractGrinderClassPathProcessor.class.getClassLoader()); StringBuilder builder = new StringBuilder(); - for (URL each : urLs) { + for (URL each : urls) { builder.append(decodePathWithUTF8(each.getFile())).append(File.pathSeparator); } return filterPatchClassPath(builder.toString(), logger); } + public static URL[] getClassPaths(ClassLoader classLoader) { + if (classLoader instanceof URLClassLoader) { + return ((URLClassLoader) classLoader).getURLs(); + } + return Stream + .of(ManagementFactory.getRuntimeMXBean().getClassPath().split(File.pathSeparator)) + .map(AbstractGrinderClassPathProcessor::toURL) + .toArray(URL[]::new); + } + + private static URL toURL(String classPathEntry) { + try { + return new File(classPathEntry).toURI().toURL(); + } catch (MalformedURLException ex) { + throw new IllegalArgumentException( + "URL could not be created from '" + classPathEntry + "'", ex); + } + } + /** * Construct classPath from current classLoader. * @@ -239,7 +263,7 @@ public String buildPatchClasspathBasedOnCurrentClassLoader(Logger logger) { * @return classpath optimized for grinder. */ public String buildClasspathBasedOnCurrentClassLoader(Logger logger) { - URL[] urls = ((URLClassLoader) AbstractGrinderClassPathProcessor.class.getClassLoader()).getURLs(); + URL[] urls = getClassPaths(AbstractGrinderClassPathProcessor.class.getClassLoader()); StringBuilder builder = new StringBuilder(); for (URL each : urls) { diff --git a/ngrinder-core/src/main/java/org/ngrinder/NGrinderAgentStarter.java b/ngrinder-core/src/main/java/org/ngrinder/NGrinderAgentStarter.java index a2c6b9a196..e77d40f88e 100644 --- a/ngrinder-core/src/main/java/org/ngrinder/NGrinderAgentStarter.java +++ b/ngrinder-core/src/main/java/org/ngrinder/NGrinderAgentStarter.java @@ -48,6 +48,8 @@ public class NGrinderAgentStarter implements AgentConstants, CommonConstants { private static final Logger LOG = LoggerFactory.getLogger("starter"); + private static final String NETWORK_ADDRESS_CACHE_TTL_SECOND = "20"; + private AgentConfig agentConfig; private AgentControllerDaemon agentController; @@ -132,8 +134,8 @@ public void startAgent() { if (agentConfig.isConnectionMode()) { LOG.info("waiting for connection on {}:{}", agentConfig.getBroadcastIP(), agentConfig.getConnectionAgentPort()); } else { - String controllerIP = getIP(agentConfig.getControllerIP()); - agentConfig.setControllerHost(controllerIP); + String controllerIP = getIP(agentConfig.getControllerHost()); + agentConfig.setControllerIP(controllerIP); LOG.info("connecting to controller {}:{}", controllerIP, agentConfig.getControllerPort()); } @@ -204,6 +206,9 @@ public static void main(String[] args) { return; } starter.checkDuplicatedRun(startMode); + + java.security.Security.setProperty("networkaddress.cache.ttl", NETWORK_ADDRESS_CACHE_TTL_SECOND); + if (startMode.equalsIgnoreCase("agent")) { starter.startAgent(); } else if (startMode.equalsIgnoreCase("monitor")) { diff --git a/ngrinder-core/src/main/java/org/ngrinder/common/constants/GrinderConstants.java b/ngrinder-core/src/main/java/org/ngrinder/common/constants/GrinderConstants.java index 03632835c1..768e5a9187 100644 --- a/ngrinder-core/src/main/java/org/ngrinder/common/constants/GrinderConstants.java +++ b/ngrinder-core/src/main/java/org/ngrinder/common/constants/GrinderConstants.java @@ -41,6 +41,7 @@ public interface GrinderConstants { String GRINDER_PROP_THREAD_RAMPUP ="grinder.threadRampUp"; String GRINDER_PROP_TEST_ID = "grinder.test.id"; String GRINDER_PROP_IGNORE_SAMPLE_COUNT = "grinder.ignoreSampleCount"; + String GRINDER_PROP_CONNECTION_RESET = "grinder.connectionReset"; String GRINDER_PROP_SECURITY = "grinder.security"; String GRINDER_PROP_SECURITY_LEVEL = "grinder.security.level"; String GRINDER_PROP_USER = "grinder.user"; diff --git a/ngrinder-controller/src/main/java/org/ngrinder/common/util/TypeConvertUtils.java b/ngrinder-core/src/main/java/org/ngrinder/common/util/TypeConvertUtils.java similarity index 89% rename from ngrinder-controller/src/main/java/org/ngrinder/common/util/TypeConvertUtils.java rename to ngrinder-core/src/main/java/org/ngrinder/common/util/TypeConvertUtils.java index ad9a113581..e50cc3e994 100644 --- a/ngrinder-controller/src/main/java/org/ngrinder/common/util/TypeConvertUtils.java +++ b/ngrinder-core/src/main/java/org/ngrinder/common/util/TypeConvertUtils.java @@ -1,4 +1,4 @@ -/* +/* * 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 @@ -9,15 +9,10 @@ * 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. + * limitations under the License. */ package org.ngrinder.common.util; -/** - * Convenient type conversion utility. - * - * @since 3.1 - */ public abstract class TypeConvertUtils { /** diff --git a/ngrinder-core/src/main/java/org/ngrinder/infra/AgentConfig.java b/ngrinder-core/src/main/java/org/ngrinder/infra/AgentConfig.java index 19a5c9e0f5..acfca228f4 100644 --- a/ngrinder-core/src/main/java/org/ngrinder/infra/AgentConfig.java +++ b/ngrinder-core/src/main/java/org/ngrinder/infra/AgentConfig.java @@ -37,6 +37,7 @@ import static net.grinder.util.NetworkUtils.DEFAULT_LOCAL_HOST_ADDRESS; import static org.apache.commons.io.FileUtils.deleteQuietly; +import static org.apache.commons.lang.StringUtils.defaultIfEmpty; import static org.apache.commons.lang.StringUtils.trimToEmpty; import static org.ngrinder.common.util.ExceptionUtils.processException; import static org.ngrinder.common.util.NoOp.noOp; @@ -59,6 +60,7 @@ public class AgentConfig implements AgentConstants, MonitorConstants, CommonCons private PropertiesWrapper monitorProperties; private PropertiesWrapper commonProperties; private PropertiesWrapper internalProperties; + private String controllerIP; private final PropertiesKeyMapper internalPropertyMapper = PropertiesKeyMapper.create("internal-properties.map"); private final PropertiesKeyMapper agentPropertyMapper = PropertiesKeyMapper.create("agent-properties.map"); @@ -318,12 +320,16 @@ public String getMonitorBindingIP() { return getMonitorProperties().getProperty(PROP_MONITOR_BINDING_IP); } - public String getControllerIP() { + public String getControllerHost() { return getAgentProperties().getProperty(PROP_AGENT_CONTROLLER_HOST, DEFAULT_LOCAL_HOST_ADDRESS); } - public void setControllerHost(String host) { - getAgentProperties().addProperty(PROP_AGENT_CONTROLLER_HOST, host); + public void setControllerIP(String ip) { + controllerIP = ip; + } + + public String getControllerIP() { + return defaultIfEmpty(controllerIP, DEFAULT_LOCAL_HOST_ADDRESS); } public int getControllerPort() { diff --git a/ngrinder-core/src/main/java/org/ngrinder/model/PerfTest.java b/ngrinder-core/src/main/java/org/ngrinder/model/PerfTest.java index 26900dbf5c..82974885ac 100644 --- a/ngrinder-core/src/main/java/org/ngrinder/model/PerfTest.java +++ b/ngrinder-core/src/main/java/org/ngrinder/model/PerfTest.java @@ -30,12 +30,12 @@ import java.util.List; import java.util.SortedSet; -import static com.sun.jmx.mbeanserver.Util.cast; import static java.util.Date.from; import static org.apache.commons.lang.ObjectUtils.defaultIfNull; import static org.ngrinder.common.util.AccessUtils.getSafe; import static org.ngrinder.common.util.DateUtils.dateToString; import static org.ngrinder.common.util.DateUtils.ms2Time; +import static org.ngrinder.common.util.TypeConvertUtils.cast; /** * Performance Test Entity. @@ -257,6 +257,11 @@ public PerfTest(User createdUser) { @Type(type = "true_false") private Boolean ignoreTooManyError; + @Column(name = "connection_reset", columnDefinition = "char(1)") + @Cloneable + @Type(type = "true_false") + private Boolean connectionReset; + @JsonIgnore @Transient private GrinderProperties grinderProperties; @@ -310,6 +315,7 @@ public void init() { this.vuserPerAgent = getSafe(this.vuserPerAgent, 1); this.safeDistribution = getSafe(this.safeDistribution, false); this.ignoreTooManyError = getSafe(this.ignoreTooManyError, false); + this.connectionReset = getSafe(this.connectionReset, true); this.useRampUp = getSafe(this.useRampUp, false); this.rampUpInitCount = getSafe(this.rampUpInitCount, 0); this.rampUpStep = getSafe(this.rampUpStep, 1); @@ -455,5 +461,6 @@ public void prepare(boolean isClone) { this.useRampUp = getSafe(this.useRampUp); this.safeDistribution = getSafe(this.safeDistribution); this.ignoreTooManyError = getSafe(this.ignoreTooManyError); + this.connectionReset = getSafe(this.connectionReset); } } diff --git a/ngrinder-core/src/main/java/org/ngrinder/model/Status.java b/ngrinder-core/src/main/java/org/ngrinder/model/Status.java index 4e570b42a5..c511825b37 100644 --- a/ngrinder-core/src/main/java/org/ngrinder/model/Status.java +++ b/ngrinder-core/src/main/java/org/ngrinder/model/Status.java @@ -211,10 +211,10 @@ private static boolean isWorkingStatus(Status status) { * * @return status list */ - public static Status[] getTestingTestStates() { + public static Status[] getTestStatesByCategory(StatusCategory statusCategory) { List status = new ArrayList<>(); for (Status each : values()) { - if (each.getCategory() == StatusCategory.TESTING) { + if (each.getCategory().equals(statusCategory)) { status.add(each); } } diff --git a/ngrinder-core/src/main/java/org/ngrinder/service/IPerfTestService.java b/ngrinder-core/src/main/java/org/ngrinder/service/IPerfTestService.java index e2220c69bd..29895cf650 100644 --- a/ngrinder-core/src/main/java/org/ngrinder/service/IPerfTestService.java +++ b/ngrinder-core/src/main/java/org/ngrinder/service/IPerfTestService.java @@ -117,6 +117,13 @@ public interface IPerfTestService { */ List getAllTesting(); + /** + * Get currently progressing PerfTest. + * + * @return found {@link PerfTest} list + */ + List getAllProgressing(); + /** * Get PerfTest Directory in which the distributed file is stored. * diff --git a/ngrinder-core/src/test/java/net/grinder/engine/agent/SingleConsoleThreadTest.java b/ngrinder-core/src/test/java/net/grinder/engine/agent/SingleConsoleThreadTest.java index b369387fc6..e584323635 100644 --- a/ngrinder-core/src/test/java/net/grinder/engine/agent/SingleConsoleThreadTest.java +++ b/ngrinder-core/src/test/java/net/grinder/engine/agent/SingleConsoleThreadTest.java @@ -66,7 +66,7 @@ public void testConsole() { agentThread2.run(console1.getConsolePort()); // Wait until all agents are started. They will connect main console. - waitAndAssertUntilAgentAttachedTo(console1, 2, 15); + waitAndAssertUntilAgentAttachedTo(console1, 2, 20); // if we shut down one agent. agentThread2.addListener(new AgentShutDownSynchronizeListener(condition)); diff --git a/ngrinder-frontend/build.gradle b/ngrinder-frontend/build.gradle index 7933219052..0ece6857f0 100644 --- a/ngrinder-frontend/build.gradle +++ b/ngrinder-frontend/build.gradle @@ -1,9 +1,9 @@ plugins { - id "com.github.node-gradle.node" version "2.2.2" + id "com.github.node-gradle.node" version "2.2.4" } node { - version = "12.16.2" + version = "14.15.4" // Enabled the automatic download. False is the default (for now). download = true } @@ -14,3 +14,8 @@ task webpack(dependsOn: "npmInstall", type: NodeTask) { args = ["--$profile"] } } + +task checkES5(type: NpxTask) { + command = "es-check@$esCheckModuleVersion" + args = ["es5", "../ngrinder-controller/build/classes/main/static/js/app.js", "--verbose"] +} diff --git a/ngrinder-frontend/package.json b/ngrinder-frontend/package.json index 0fe2f4745c..033d249ebf 100644 --- a/ngrinder-frontend/package.json +++ b/ngrinder-frontend/package.json @@ -1,6 +1,6 @@ { "name": "ngrinder", - "version": "3.5.3", + "version": "3.5.4", "description": "ngrinder GUI", "private": true, "repository": { @@ -19,7 +19,7 @@ "dependencies": { "html-entities": "1.3.1", "select2": "^3.5.1", - "axios": "^0.19.0", + "axios": "^0.21.1", "base64-js": "^1.2.1", "billboard.js": "^1.11.0", "bootbox": "^5.2.0", @@ -35,7 +35,6 @@ "jshint": "^2.9.6", "jstz": "^2.1.1", "js-yaml": "^3.13.1", - "jsonc": "^2.0.0", "lodash": "^4.17.11", "moment": "^2.24.0", "moment-duration-format": "^2.2.2", @@ -50,7 +49,6 @@ "vuejs-datepicker": "^1.6.2", "vee-validate": "^2.2.0", "vue": "^2.6.10", - "vue-authenticate": "^1.3.4", "vue-bootstrap-slider": "^2.1.8", "vue-clipboard2": "0.0.8", "vue-codemirror": "^4.0.6", @@ -74,22 +72,22 @@ "vuex": "^3.0.0" }, "devDependencies": { - "@babel/core": "^7.6.2", - "@babel/plugin-proposal-class-properties": "^7.5.5", - "@babel/plugin-proposal-decorators": "^7.6.0", - "@babel/plugin-syntax-dynamic-import": "^7.2.0", - "@babel/plugin-transform-runtime": "^7.6.2", - "@babel/polyfill": "^7.6.0", - "@babel/preset-env": "^7.6.2", - "@babel/plugin-transform-for-of": "^7.7.4", + "@babel/core": "^7.12.10", + "@babel/plugin-proposal-class-properties": "^7.12.1", + "@babel/plugin-proposal-decorators": "^7.12.12", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-transform-runtime": "^7.12.10", + "@babel/polyfill": "^7.12.1", + "@babel/preset-env": "^7.12.11", + "@babel/plugin-transform-for-of": "^7.12.1", "babel-cli": "^6.26.0", - "babel-eslint": "^8.2.3", - "babel-loader": "^8.0.6", + "babel-eslint": "^10.1.0", + "babel-loader": "^8.2.2", "babel-plugin-component": "^1.1.1", "babel-register": "^6.26.0", "copy-webpack-plugin": "^5.0.4", "css-loader": "^3.5.2", - "eslint": "^4.19.1", + "eslint": "^5.16.0", "eslint-config-naver": "^2.1.0", "eslint-plugin-vue": "^5.2.3", "expose-loader": "^0.7.5", diff --git a/ngrinder-frontend/src/img/ball/blue_anime.gif b/ngrinder-frontend/src/img/ball/blue_anime.gif index 6c4a29c442..7e5c700e7e 100644 Binary files a/ngrinder-frontend/src/img/ball/blue_anime.gif and b/ngrinder-frontend/src/img/ball/blue_anime.gif differ diff --git a/ngrinder-frontend/src/img/ball/green_anime.gif b/ngrinder-frontend/src/img/ball/green_anime.gif index 62babbd2c3..91c7255416 100644 Binary files a/ngrinder-frontend/src/img/ball/green_anime.gif and b/ngrinder-frontend/src/img/ball/green_anime.gif differ diff --git a/ngrinder-frontend/src/img/ball/yellow_anime.gif b/ngrinder-frontend/src/img/ball/yellow_anime.gif index 441e59150c..7958173923 100644 Binary files a/ngrinder-frontend/src/img/ball/yellow_anime.gif and b/ngrinder-frontend/src/img/ball/yellow_anime.gif differ diff --git a/ngrinder-frontend/src/js/components/Login.vue b/ngrinder-frontend/src/js/components/Login.vue index e080f4d68d..954d0c1237 100644 --- a/ngrinder-frontend/src/js/components/Login.vue +++ b/ngrinder-frontend/src/js/components/Login.vue @@ -17,7 +17,7 @@
- + Remember Me + +
+
+ + +
- - + +
-
- - - -
diff --git a/ngrinder-frontend/src/js/components/perftest/detail/Detail.vue b/ngrinder-frontend/src/js/components/perftest/detail/Detail.vue index 8d34cc4b53..415273be0e 100644 --- a/ngrinder-frontend/src/js/components/perftest/detail/Detail.vue +++ b/ngrinder-frontend/src/js/components/perftest/detail/Detail.vue @@ -137,6 +137,7 @@ samplingInterval: test.config.samplingInterval, ignoreSampleCount: test.config.ignoreSampleCount, ignoreTooManyError: test.config.ignoreTooManyError, + connectionReset: test.config.connectionReset, safeDistribution: test.config.safeDistribution, param: test.config.param, scm: test.config.scm, @@ -178,6 +179,7 @@ samplingInterval: test.samplingInterval, ignoreSampleCount: test.ignoreSampleCount, ignoreTooManyError: test.ignoreTooManyError, + connectionReset: test.connectionReset, safeDistribution: test.safeDistribution, param: test.param, scm: test.scm || 'svn', diff --git a/ngrinder-frontend/src/js/components/perftest/list/Searchbar.vue b/ngrinder-frontend/src/js/components/perftest/list/Searchbar.vue index d105667860..2914234a2a 100644 --- a/ngrinder-frontend/src/js/components/perftest/list/Searchbar.vue +++ b/ngrinder-frontend/src/js/components/perftest/list/Searchbar.vue @@ -1,7 +1,7 @@