Hi,

JMeter doesn't provide that functionality out of the box. If you are able to program you token refresh in groovy (or any other JSR223 Sampler), you can take a look at the attached test plan.

That test plan creates a daemon thread from a JSR223 Groovy Sampler that refreshes ONE token every 50 minutes for as long the thread is running. That happens in a setup thread group and therefore before any other thread group.

In the two simulated thread groups I placed a JSR223 Groovy Sampler to get the token from the props variable (it is currently a AtomicReference to make it safer for JMeters access model). Then a http sampler (that will fail because of an invalid DNS name) uses that token.

If thread group uses a lot of time for each round, place the get token logic inside a JSR223 Timer. That should run before each sampler on the same level or downwards.

When all thread groups are done the teardown thread group will try to stop the daemon thread with another groovy script.

Maybe that helps you as a starting point.

Felix

Am 27.02.26 um 16:45 schrieb Abhitosh Patil:
Hi,

I have 4 thread groups in my JMeter script.
Run Thread Groups Consecutively (One at a time) option is ON.

Now I want to add 1 more thread group at the top for session token
generation. It should generate fresh session token after every 55 minutes.

I can't uncheck Run Thread Groups Consecutively option.

How can I handle this?

Thanks in advance.

Abhitosh Patil

<?xml version='1.0' encoding='UTF-8'?>
<jmeterTestPlan version="1.2" properties="5.0" jmeter="6.0.0-SNAPSHOT cfdc1b3">
  <hashTree>
    <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Testplan">
      <boolProp name="TestPlan.serialize_threadgroups">true</boolProp>
      <boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp>
      <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="Benutzer definierte Variablen">
        <collectionProp name="Arguments.arguments"/>
      </elementProp>
    </TestPlan>
    <hashTree>
      <ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree">
        <boolProp name="ResultCollector.error_logging">false</boolProp>
        <objProp>
          <name>saveConfig</name>
          <value class="SampleSaveConfiguration">
            <time>true</time>
            <latency>true</latency>
            <timestamp>true</timestamp>
            <success>true</success>
            <label>true</label>
            <code>true</code>
            <message>true</message>
            <threadName>true</threadName>
            <dataType>true</dataType>
            <encoding>false</encoding>
            <assertions>true</assertions>
            <subresults>true</subresults>
            <responseData>false</responseData>
            <samplerData>false</samplerData>
            <xml>false</xml>
            <fieldNames>true</fieldNames>
            <responseHeaders>false</responseHeaders>
            <requestHeaders>false</requestHeaders>
            <responseDataOnError>false</responseDataOnError>
            <saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
            <assertionsResultsToSave>0</assertionsResultsToSave>
            <bytes>true</bytes>
            <sentBytes>true</sentBytes>
            <url>true</url>
            <threadCounts>true</threadCounts>
            <idleTime>true</idleTime>
            <connectTime>true</connectTime>
          </value>
        </objProp>
        <stringProp name="filename"/>
      </ResultCollector>
      <hashTree/>
      <SetupThreadGroup guiclass="SetupThreadGroupGui" testclass="SetupThreadGroup" testname="setUp Thread Group">
        <intProp name="ThreadGroup.num_threads">1</intProp>
        <intProp name="ThreadGroup.ramp_time">1</intProp>
        <boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
        <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
        <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Schleifen-Controller (Loop Controller)">
          <stringProp name="LoopController.loops">1</stringProp>
          <boolProp name="LoopController.continue_forever">false</boolProp>
        </elementProp>
      </SetupThreadGroup>
      <hashTree>
        <JSR223Sampler guiclass="TestBeanGUI" testclass="JSR223Sampler" testname="Start Daemon">
          <stringProp name="cacheKey">true</stringProp>
          <stringProp name="filename"/>
          <stringProp name="parameters"/>
          <stringProp name="script">import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicReference
import java.util.concurrent.atomic.AtomicLong

def tokenRef = new AtomicReference&lt;String>()
def lastRefreshTime = new AtomicLong(System.currentTimeMillis())

// Token refresh function (replace with your auth logic)
def refreshToken = {
    try {
        // 🔐 Example: Call your auth endpoint here
        // For demo: generate a dummy token + timestamp
        def newToken = "Bearer_" + System.currentTimeMillis() + "_" + (Math.random() * 10000).toInteger()
        def now = System.currentTimeMillis()

        // Lock-free atomic updates
        tokenRef.set(newToken)
        lastRefreshTime.set(now)

        log.info("[Token Daemon] Token refreshed at {}", new Date(now))
        log.debug("New token (truncated): {}...", newToken.take(20))
    } catch (Exception e) {
        log.error("[Token Daemon] Token refresh failed", e)
    }
}

// Create daemon thread (non-blocking, won't prevent JMeter shutdown)
def daemon = Executors.newSingleThreadScheduledExecutor { r ->
    def t = new Thread(r)
    t.setName("TokenRefresh-Daemon")
    t.setDaemon(true)  // ✅ Critical: allow JMeter to exit
    t
}

// Schedule: refresh every 50 minutes (3000 sec), start immediately
daemon.scheduleWithFixedDelay(refreshToken, 0, 50 * 60, TimeUnit.SECONDS)

// Expose via props for reuse in other TGs
props.put('TOKEN_REF', tokenRef)
props.put('TOKEN_TIMESTAMP_REF', lastRefreshTime)
props.put('TOKEN_DAEMON', daemon)

log.info("[Token Daemon] Started — next refresh in ~50 min")</stringProp>
          <stringProp name="scriptLanguage">groovy</stringProp>
        </JSR223Sampler>
        <hashTree/>
      </hashTree>
      <PostThreadGroup guiclass="PostThreadGroupGui" testclass="PostThreadGroup" testname="tearDown Thread Group">
        <intProp name="ThreadGroup.num_threads">1</intProp>
        <intProp name="ThreadGroup.ramp_time">1</intProp>
        <boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
        <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
        <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Schleifen-Controller (Loop Controller)">
          <stringProp name="LoopController.loops">1</stringProp>
          <boolProp name="LoopController.continue_forever">false</boolProp>
        </elementProp>
      </PostThreadGroup>
      <hashTree>
        <JSR223Sampler guiclass="TestBeanGUI" testclass="JSR223Sampler" testname="Stop Daemon">
          <stringProp name="scriptLanguage">groovy</stringProp>
          <stringProp name="parameters"/>
          <stringProp name="filename"/>
          <stringProp name="cacheKey">true</stringProp>
          <stringProp name="script">import java.util.concurrent.TimeUnit

def daemon = props.get('TOKEN_DAEMON')
if (daemon) {
    log.info("[Shutdown] Stopping token refresh daemon...")
    daemon.shutdown()
    if (!daemon.awaitTermination(5, TimeUnit.SECONDS)) {
        daemon.shutdownNow()
        if (!daemon.awaitTermination(3, TimeUnit.SECONDS)) {
            log.error("Daemon did not terminate!")
        }
    }
    log.info("[Shutdown] Daemon stopped cleanly")
} else {
    log.warn("No daemon found to stop")
}</stringProp>
        </JSR223Sampler>
        <hashTree/>
      </hashTree>
      <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread-Group ONE">
        <intProp name="ThreadGroup.num_threads">2</intProp>
        <intProp name="ThreadGroup.ramp_time">5</intProp>
        <boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
        <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
        <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Schleifen-Controller (Loop Controller)">
          <stringProp name="LoopController.loops">2</stringProp>
          <boolProp name="LoopController.continue_forever">false</boolProp>
        </elementProp>
      </ThreadGroup>
      <hashTree>
        <JSR223Sampler guiclass="TestBeanGUI" testclass="JSR223Sampler" testname="Get Token from Daemon ONE">
          <stringProp name="scriptLanguage">groovy</stringProp>
          <stringProp name="parameters"/>
          <stringProp name="filename"/>
          <stringProp name="cacheKey">true</stringProp>
          <stringProp name="script">import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicReference
import java.util.concurrent.atomic.AtomicLong

def tokenRef = props.get('TOKEN_REF') as AtomicReference&lt;String>
def timeRef = props.get('TOKEN_TIMESTAMP_REF') as AtomicLong

if (!tokenRef) {
    log.error("❌ Token ref not found! Did setUpTG run?")
    throw new IllegalStateException("Token manager not initialized")
}

def token = tokenRef.get()
if (!token) {
    log.warn("⚠️ Token is null — waiting for first refresh (daemon may be starting)")
}

def last = timeRef?.get() ?: 0L
def ageMin = (System.currentTimeMillis() - last) / 60000.0

vars.put('SESSION_TOKEN', token)
log.info("[TG1] Token loaded (age: {} min) | Active {}", ageMin.round(2), !token?.isEmpty())</stringProp>
        </JSR223Sampler>
        <hashTree/>
        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Use Token ONE">
          <stringProp name="HTTPSampler.domain">some.server.invalid</stringProp>
          <stringProp name="HTTPSampler.protocol">https</stringProp>
          <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
          <stringProp name="HTTPSampler.method">GET</stringProp>
          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
          <boolProp name="HTTPSampler.postBodyRaw">false</boolProp>
          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="Benutzer definierte Variablen">
            <collectionProp name="Arguments.arguments">
              <elementProp name="TOKEN" elementType="HTTPArgument">
                <boolProp name="HTTPArgument.always_encode">false</boolProp>
                <stringProp name="Argument.value">${SESSION_TOKEN}</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
                <boolProp name="HTTPArgument.use_equals">true</boolProp>
                <stringProp name="Argument.name">TOKEN</stringProp>
              </elementProp>
            </collectionProp>
          </elementProp>
        </HTTPSamplerProxy>
        <hashTree/>
      </hashTree>
      <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread-Group TWO">
        <intProp name="ThreadGroup.num_threads">2</intProp>
        <intProp name="ThreadGroup.ramp_time">5</intProp>
        <boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
        <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
        <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Schleifen-Controller (Loop Controller)">
          <stringProp name="LoopController.loops">2</stringProp>
          <boolProp name="LoopController.continue_forever">false</boolProp>
        </elementProp>
      </ThreadGroup>
      <hashTree>
        <JSR223Sampler guiclass="TestBeanGUI" testclass="JSR223Sampler" testname="Get Token from Daemon TWO">
          <stringProp name="scriptLanguage">groovy</stringProp>
          <stringProp name="parameters"/>
          <stringProp name="filename"/>
          <stringProp name="cacheKey">true</stringProp>
          <stringProp name="script">import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicReference
import java.util.concurrent.atomic.AtomicLong

def tokenRef = props.get('TOKEN_REF') as AtomicReference&lt;String>
def timeRef = props.get('TOKEN_TIMESTAMP_REF') as AtomicLong

if (!tokenRef) {
    log.error("❌ Token ref not found! Did setUpTG run?")
    throw new IllegalStateException("Token manager not initialized")
}

def token = tokenRef.get()
if (!token) {
    log.warn("⚠️ Token is null — waiting for first refresh (daemon may be starting)")
}

def last = timeRef?.get() ?: 0L
def ageMin = (System.currentTimeMillis() - last) / 60000.0

vars.put('SESSION_TOKEN', token)
log.info("✅ [TG1] Token loaded (age: {} min) | Active {}", ageMin.round(2), !token?.isEmpty())</stringProp>
        </JSR223Sampler>
        <hashTree/>
        <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Use Token TWO">
          <stringProp name="HTTPSampler.domain">some.server.invalid</stringProp>
          <stringProp name="HTTPSampler.protocol">https</stringProp>
          <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
          <stringProp name="HTTPSampler.method">GET</stringProp>
          <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
          <boolProp name="HTTPSampler.postBodyRaw">false</boolProp>
          <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="Benutzer definierte Variablen">
            <collectionProp name="Arguments.arguments">
              <elementProp name="TOKEN" elementType="HTTPArgument">
                <boolProp name="HTTPArgument.always_encode">false</boolProp>
                <stringProp name="Argument.value">${SESSION_TOKEN}</stringProp>
                <stringProp name="Argument.metadata">=</stringProp>
                <boolProp name="HTTPArgument.use_equals">true</boolProp>
                <stringProp name="Argument.name">TOKEN</stringProp>
              </elementProp>
            </collectionProp>
          </elementProp>
        </HTTPSamplerProxy>
        <hashTree/>
      </hashTree>
    </hashTree>
  </hashTree>
</jmeterTestPlan>

Attachment: OpenPGP_0xEA6C3728EA91C4AF.asc
Description: OpenPGP public key

Attachment: OpenPGP_signature.asc
Description: OpenPGP digital signature

Reply via email to