Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/gradle-dependency-submit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ permissions: read-all

jobs:
dependency-submission:
if: github.repository == 'apache/jmeter'
name: Submit dependencies
runs-on: ubuntu-latest
permissions:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/gradle-wrapper-validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ on: [push, pull_request]

jobs:
validation:
if: github.repository == 'apache/jmeter'
name: "Validation"
runs-on: ubuntu-latest
steps:
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
name: CI

# Forks: skip all jobs (secrets, dependency graph, etc. are not fork-friendly).
# PRs into apache/jmeter still run CI in the base repo, where github.repository is apache/jmeter.

on:
push:
branches:
Expand All @@ -19,6 +22,7 @@ concurrency:

jobs:
matrix_prep:
if: github.repository == 'apache/jmeter'
name: Matrix Preparation
runs-on: ubuntu-latest
outputs:
Expand All @@ -33,6 +37,7 @@ jobs:
node .github/workflows/matrix.js

test:
if: github.repository == 'apache/jmeter'
needs: matrix_prep
name: '${{ matrix.name }}'
runs-on: ${{ matrix.os }}
Expand Down Expand Up @@ -89,6 +94,7 @@ jobs:
DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}

errorprone:
if: github.repository == 'apache/jmeter'
name: 'Error Prone (JDK 21)'
runs-on: ubuntu-latest
steps:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release-drafter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ permissions: read-all

jobs:
update_release_draft:
# Skip release drafts in forks
if: vars.RUN_RELEASE_DRAFTER == 'true'
# Skip outside apache/jmeter; on Apache, opt-in via repo variable.
if: github.repository == 'apache/jmeter' && vars.RUN_RELEASE_DRAFTER == 'true'
name: Update Release Draft
runs-on: ubuntu-latest
permissions:
Expand Down
15 changes: 15 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@
/buildSrc/subprojects/*/build/
/buildSrc/subprojects/*/bin/
/buildSrc/subprojects/*/out/
# Included Gradle build logic: ignore outputs only (sources under build-logic* must stay tracked)
/build-logic/build/
/build-logic/*/build/
/build-logic/*/bin/
/build-logic/*/out/
/build-logic-commons/build/
/build-logic-commons/*/build/
/build-logic-commons/*/bin/
/build-logic-commons/*/out/
/src/build/
/src/bin/
/src/out/
Expand Down Expand Up @@ -49,9 +58,13 @@
.pmd
.project
.settings
.metals
.vscode

# bin/ folder is used to launch JMeter, so we ignore certain files there
/bin/ApacheJMeter.jar
# Local JMeter Plugins Manager CLI wrappers (not part of Apache distribution)
/bin/PluginsManagerCMD.*
# Below are the results of "batch test" execution
/bin/*.csv
/bin/*.jmx
Expand All @@ -63,6 +76,8 @@

build-local.properties
jmeter-fb.*
# Local packaging script output (see create_jmeter_archive.sh)
/jmeter_*.tgz

# ignore generated keystore and certs
/bin/proxyserver.jks
Expand Down
12 changes: 12 additions & 0 deletions bin/jmeter.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1173,6 +1173,18 @@ cookies=cookies

# How often to check for shutdown during ramp-up (milliseconds)
#jmeterthread.rampup.granularity=1000
# How often to check for shutdown during timer delay (milliseconds)
#jmeterthread.timer.granularity=1000

# Enable Java 21 Virtual Threads for JMeter threads
# When enabled, virtual user threads use lightweight virtual threads instead of platform threads
# Benefits: Lower memory per thread, better I/O scalability for high thread counts
# Requires: Java 21+ (automatically falls back to platform threads on older Java versions)
# Default: true
jmeter.threads.virtual.enabled=true

# Whether to enable lightweight clone for test elements to preserve memory. Disable when running tests with complex scenarios with variables.
#jmeter.clone.lightweight.enabled=true

#Should JMeter expand the tree when loading a test plan?
# default value is false since JMeter 2.7
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,12 @@ plugins.withId("org.jetbrains.kotlin.jvm") {
kotlin {
license()
trimTrailingWhitespace()
ktlint("0.40.0")
ktlint("0.40.0") {
filter {
// TODO: remove exclusion when update ktlint
exclude("**/org/apache/jorphan/locale/PlainValue.kt")
}
}
endWithNewline()
}
}
Expand Down
43 changes: 43 additions & 0 deletions create_jmeter_archive.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/bin/bash

# Script to create a compressed tar archive of bin and lib directories
# Excludes bin/examples and bin/testfiles
# Archives files under apache-jmeter/ directory

# Generate datetime string in format YYYYMMDD_HHMMSS
DATETIME=$(date +%Y%m%d_%H%M%S)

# Archive name
ARCHIVE_NAME="jmeter_${DATETIME}.tgz"

# Create temporary directory
TEMP_DIR=$(mktemp -d)
trap "rm -rf ${TEMP_DIR}" EXIT

# Create apache-jmeter directory structure
mkdir -p "${TEMP_DIR}/apache-jmeter"

# Copy bin directory excluding examples and testfiles
# Using rsync for better exclusion control
rsync -av --exclude='examples' --exclude='testfiles' \
bin/ "${TEMP_DIR}/apache-jmeter/bin/"

# Copy lib directory
cp -R lib "${TEMP_DIR}/apache-jmeter/"

# Create the archive from temp directory
# -C: change to directory before archiving
# -c: create archive
# -z: compress with gzip
# -f: specify filename
tar -czf "${ARCHIVE_NAME}" -C "${TEMP_DIR}" apache-jmeter

# Check if tar command succeeded
if [ $? -eq 0 ]; then
echo "Successfully created archive: ${ARCHIVE_NAME}"
# Show archive size
ls -lh "${ARCHIVE_NAME}"
else
echo "Error: Failed to create archive"
exit 1
fi
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
import org.apache.jmeter.threads.JMeterContext;
import org.apache.jmeter.threads.JMeterVariables;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.util.EnumUtils;
import org.apache.jorphan.locale.ResourceKeyed;
import org.apache.jorphan.util.JMeterStopThreadException;
import org.apache.jorphan.util.JOrphanUtils;
import org.apache.jorphan.util.StringUtilities;
Expand Down Expand Up @@ -70,20 +70,20 @@
public class CSVDataSet extends ConfigTestElement
implements TestBean, LoopIterationListener, NoConfigMerge {

public enum ShareMode {
public enum ShareMode implements ResourceKeyed {
ALL("shareMode.all"),
GROUP("shareMode.group"),
THREAD("shareMode.thread");

private final String value;
private final String propertyName;

ShareMode(String value) {
this.value = value;
ShareMode(String propertyName) {
this.propertyName = propertyName;
}

@Override
public String toString() {
return value;
public String getResourceKey() {
return propertyName;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.apache.jmeter.threads.JMeterContextService;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.collections.IdentityKey;
import org.apache.jorphan.locale.ResourceKeyed;
import org.apache.jorphan.util.EnumUtils;
import org.apiguardian.api.API;

Expand Down Expand Up @@ -71,7 +72,7 @@ private static class ThroughputInfo{
/**
* This enum defines the calculation modes used by the ConstantThroughputTimer.
*/
public enum Mode {
public enum Mode implements ResourceKeyed {
ThisThreadOnly("calcMode.1"), // NOSONAR Keep naming for compatibility
AllActiveThreads("calcMode.2"), // NOSONAR Keep naming for compatibility
AllActiveThreadsInCurrentThreadGroup("calcMode.3"), // NOSONAR Keep naming for compatibility
Expand All @@ -89,6 +90,11 @@ public enum Mode {
public String toString() {
return propertyName;
}

@Override
public String getResourceKey() {
return propertyName;
}
}

/**
Expand Down Expand Up @@ -161,15 +167,13 @@ public Mode getMode() {
}

@Deprecated
@SuppressWarnings("EnumOrdinal")
public void setCalcMode(int mode) {
setMode(EnumUtils.values(Mode.class).get(mode));
setMode(EnumUtils.getEnumValues(Mode.class).get(mode));
}

@SuppressWarnings("EnumOrdinal")
@API(status = API.Status.MAINTAINED, since = "6.0.0")
public void setMode(Mode newMode) {
getSchema().getCalcMode().set(this, newMode.toString());
getSchema().getCalcMode().set(this, newMode.getResourceKey());
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ dependencies {
isTransitive = false
}
implementation("org.apache.xmlgraphics:xmlgraphics-commons")
implementation("org.brotli:dec")
implementation("org.freemarker:freemarker")
implementation("org.jodd:jodd-core")
implementation("org.jodd:jodd-props")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,20 @@

import java.io.Serializable;

import org.apache.jmeter.engine.util.LightweightClone;
import org.apache.jmeter.testelement.AbstractTestElement;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.schema.PropertiesAccessor;

public class ConfigTestElement extends AbstractTestElement implements Serializable, ConfigElement {
/**
* Base class for configuration elements that can share properties across threads.
* <p>
* ConfigTestElements implementing {@link LightweightClone} will only use lightweight
* cloning if they have no properties containing JMeter variables (${...}) or functions (__()).
* This provides significant memory savings for elements like HeaderManager with static data.
* </p>
*/
public class ConfigTestElement extends AbstractTestElement implements Serializable, ConfigElement, LightweightClone {
private static final long serialVersionUID = 240L;

public static final String USERNAME = "ConfigTestElement.username";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,20 @@ public class TestPlanGui extends AbstractJMeterGuiComponent {
private final JBooleanPropertyEditor functionalMode =
new JBooleanPropertyEditor(
TestPlanSchema.INSTANCE.getFunctionalMode(),
JMeterUtils.getResString("functional_mode"));
"functional_mode",
JMeterUtils::getResString);

private final JBooleanPropertyEditor serializedMode =
new JBooleanPropertyEditor(
TestPlanSchema.INSTANCE.getSerializeThreadgroups(),
JMeterUtils.getResString("testplan.serialized"));
"testplan.serialized",
JMeterUtils::getResString);

private final JBooleanPropertyEditor tearDownOnShutdown =
new JBooleanPropertyEditor(
TestPlanSchema.INSTANCE.getTearDownOnShutdown(),
JMeterUtils.getResString("teardown_on_shutdown"));
"teardown_on_shutdown",
JMeterUtils::getResString);

/** A panel allowing the user to define variables. */
private final ArgumentsPanel argsPanel;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,15 @@ public class TransactionControllerGui extends AbstractControllerGui {
private final JBooleanPropertyEditor generateParentSample =
new JBooleanPropertyEditor(
TransactionControllerSchema.INSTANCE.getGenearteParentSample(),
JMeterUtils.getResString("transaction_controller_parent"));
"transaction_controller_parent",
JMeterUtils::getResString);

/** if selected, add duration of timers to total runtime */
private final JBooleanPropertyEditor includeTimers =
new JBooleanPropertyEditor(
TransactionControllerSchema.INSTANCE.getIncludeTimers(),
JMeterUtils.getResString("transaction_controller_include_timers"));
"transaction_controller_parent",
JMeterUtils::getResString);

/**
* Create a new TransactionControllerGui instance.
Expand Down
13 changes: 13 additions & 0 deletions src/core/src/main/java/org/apache/jmeter/engine/TreeCloner.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,28 @@
import java.util.ArrayList;
import java.util.List;

import org.apache.jmeter.engine.util.LightweightClone;
import org.apache.jmeter.engine.util.NoThreadClone;
import org.apache.jmeter.testelement.AbstractTestElement;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.collections.HashTree;
import org.apache.jorphan.collections.HashTreeTraverser;
import org.apache.jorphan.collections.ListedHashTree;

/**
* Clones the test tree, skipping test elements that implement {@link NoThreadClone} by default.
* Elements implementing {@link LightweightClone} will share properties instead of deep cloning.
*/
public class TreeCloner implements HashTreeTraverser {

/**
* Property to enable/disable lightweight cloning for LightweightClone elements.
* Can be disabled by setting {@code jmeter.clone.lightweight.enabled=false} in jmeter.properties.
*/
private static final boolean LIGHTWEIGHT_CLONE_ENABLED =
JMeterUtils.getPropDefault("jmeter.clone.lightweight.enabled", true);

private final ListedHashTree newTree;

private final List<Object> objects = new ArrayList<>();
Expand Down Expand Up @@ -80,6 +91,8 @@ protected Object addNodeToTree(Object node) {
newTree.add(objects, node);
return node;
}
newTree.add(objects, node);
return node;
}

/**
Expand Down
Loading