How do you read a Perspective Session Custom Property from a Gateway Scoped Module?

Hello,

I am trying to retrieve a perspective session custom property from a gateway scoped module and I have tried to use an InternalSession like below with no success.

UUID internalUUID = InternalSession.SESSION.get().getSessionId();

PerspectiveContext pc = InternalSession.SESSION.get().getPerspectiveContext();

Optional<QualifiedValue> temp = pc.getSessionMonitor().findSession(internalUUID).get().getPropertyTreeOf(PropertyType.custom).read("custom.test123");

I was thinking that I could read the properties using this InternalSession, which has not worked, so I would like some advice or pointers to read the sessions custom property.

I'm using this question to test a prompt, so excuse me ahead of time for answering you with obvious LLM output.


Reading Perspective session custom properties from Gateway-scoped code requires:

  1. Obtaining the PerspectiveContext via PerspectiveContext.get(GatewayContext)
  2. Finding the session via SessionMonitor.findSession(UUID) or using the InternalSession.SESSION ThreadLocal if executing within a session context
  3. Accessing the property tree via session.getPropertyTreeOf(PropertyType.custom)
  4. Reading properties on the queue - PropertyTree access MUST be done on the session's execution queue

The forum user's approach was close but likely failed because PropertyTree reads must be executed on the session's queue, not directly on any thread.

Correct Implementation Pattern

From Gateway Code with Known Session UUID

import com.inductiveautomation.ignition.gateway.model.GatewayContext;
import com.inductiveautomation.perspective.gateway.api.PerspectiveContext;
import com.inductiveautomation.perspective.gateway.session.InternalSession;
import com.inductiveautomation.perspective.gateway.property.PropertyTree;
import com.inductiveautomation.perspective.common.api.PropertyType;
import com.inductiveautomation.ignition.common.QualifiedValue;

import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ExecutionException;

public Optional<QualifiedValue> readSessionCustomProperty(
        GatewayContext gatewayContext,
        UUID sessionId,
        String propertyPath) {

    // 1. Get PerspectiveContext
    PerspectiveContext perspectiveContext = PerspectiveContext.get(gatewayContext);

    // 2. Find the session by UUID
    Optional<InternalSession> sessionOpt = perspectiveContext
        .getSessionMonitor()
        .findSession(sessionId);

    if (sessionOpt.isEmpty()) {
        return Optional.empty();
    }

    InternalSession session = sessionOpt.get();

    // 3. Get the custom property tree
    PropertyTree customTree = session.getPropertyTreeOf(PropertyType.custom);
    if (customTree == null) {
        return Optional.empty();
    }

    // 4. Read property ON THE SESSION'S QUEUE (critical!)
    try {
        return customTree.getQueue()
            .runOrSubmit(() -> customTree.read(propertyPath))
            .get();
    } catch (InterruptedException | ExecutionException e) {
        return Optional.empty();
    }
}

From Within Session Context (ThreadLocal available)

When code executes within a session context (script execution, binding evaluation), the ThreadLocal is available:

public Optional<QualifiedValue> readCurrentSessionCustomProperty(String propertyPath) {
    // Get session from ThreadLocal
    InternalSession session = InternalSession.SESSION.get();
    if (session == null) {
        throw new IllegalStateException("No perspective session attached to this thread.");
    }

    PropertyTree customTree = session.getPropertyTreeOf(PropertyType.custom);
    if (customTree == null) {
        return Optional.empty();
    }

    // Read property on the session's queue
    try {
        return customTree.getQueue()
            .runOrSubmit(() -> customTree.read(propertyPath))
            .get();
    } catch (InterruptedException | ExecutionException e) {
        return Optional.empty();
    }
}

Thank you very much for your quick and detailed response.

I will give them a go now and report back here.

I tested out the two methods that you had suggested and I was getting Optional.empty which I was not expecting.

I looked into this (for the Gateway Code) and did some tests and found that the PropertyTree does have the value that I’m looking for, but does not appear to be getting parsed correctly.

When I test with:

customTree.getQueue().runOrSubmit(customTree::toJson).get().toString()

I get the result:

{"$":["map",0,1767577165100],"test123":{"$":["qv",0,1767577165100],"$v":"321tset"}}

My session custom property looks like this:

Is there something that needs to be done for the customTree.read(propertyPath) to be able to read the mapped value?

I did some more digging and found that the path only needed to include the name of the value that I had under the session custom section.

So instead of:

session.custom.test123

I used:

test123

Thank you very much for your help Kevin.

1 Like