QF-Test/Automated testing issue connecting to client

We use QF-Test to do some automated testing on some of our clients.

I haven't been able to run QF-Test the past couple of months because it can't connect to the vision client launcher anymore. It seems to be something to do with javasecurities but I am not sure. I have tried installing in different locations, disabled securities on the machine I run it on.

Has anyone by chance gone through this or have any ideas what I can try?

It used to run fine. This is the error I get in the terminal:

----- visionclientlauncher:stdout -----
INFO  [CompositeClassRejectListFilter] [2025/04/21 17:05:54]: Initialization performed successfully
INFO  [CompositeClassRejectListFilter] [2025/04/21 17:05:54]: JVM-wide ObjectInputFilter set up successfully
INFO  [CompositeClassRejectListFilter] [2025/04/21 17:05:54]: Platform serialFilter has 88 pattern(s)
INFO  [ClientLauncher                ] [2025/04/21 17:05:54]: Set initial logging level to INFO
----- visionclientlauncher:stderr -----
Apr 21, 2025 5:05:54 PM com.sun.javafx.application.PlatformImpl startup
WARNING: Unsupported JavaFX configuration: classes were loaded from 'unnamed module @3f200884'
----- visionclientlauncher:stdout -----
WARN  [CompositeClassRejectListFilter] [2025/04/21 17:05:55]: Serial class 'java.rmi.server.RemoteObject' REJECTED by Platform pattern 'java.rmi.server.RemoteObject' for security. Set log level to DEBUG to log additional details for future events.
----- visionclientlauncher:stderr -----
1 (17:05:55.443) JavaFX-Launcher de.qfs.apps.qftest.client.start.ClientStarter.startClient(Object).java.security.PrivilegedAction.run(): (#589) ex: java.rmi.UnmarshalException: error unmarshalling return; nested exception is: 
	java.io.InvalidClassException: filter status: REJECTED
2 (17:05:55.443) JavaFX-Launcher de.qfs.apps.qftest.client.start.ClientStarter.startClient(Object).java.security.PrivilegedAction.run(): (#589) java.rmi.UnmarshalException: error unmarshalling return; nested exception is: 
	java.io.InvalidClassException: filter status: REJECTED
	at java.rmi/sun.rmi.registry.RegistryImpl_Stub.lookup(RegistryImpl_Stub.java:130)
	at java.rmi/java.rmi.Naming.lookup(Naming.java:101)
	at de.qfs.apps.qftest.client.start.b.run(SourceFile:582)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:318)
	at de.qfs.apps.qftest.client.start.a.a(SourceFile:491)
	at de.qfs.apps.qftest.client.start.SUTWrapper.<init>(SourceFile:205)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481)
	at de.qfs.apps.qftest.start.Connector.maybeConnect(Connector.java:405)
	at de.qfs.apps.qftest.start.Connector.access$500(Connector.java:81)
	at de.qfs.apps.qftest.start.Connector$5.run(Connector.java:447)
	at de.qfs.apps.qftest.start.Connector.go(Connector.java:475)
	at de.qfs.apps.qftest.start.Connector.access$000(Connector.java:81)
	at de.qfs.apps.qftest.start.Connector$4.run(Connector.java:327)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:318)
	at de.qfs.apps.qftest.start.Connector.<init>(Connector.java:317)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481)
	at de.qfs.apps.qftest.qagent.InstanceWaiter$1.run(InstanceWaiter.java:228)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:318)
	at de.qfs.apps.qftest.qagent.InstanceWaiter.instanceCreated(InstanceWaiter.java:82)
	at de.qfs.apps.qftest.instrument.InstanceTracer.instanceCreated(InstanceTracer.java:206)
	at com.sun.javafx.tk.Toolkit.<init>(Toolkit.java:305)
	at com.sun.javafx.tk.quantum.QuantumToolkit.<init>(QuantumToolkit.java:138)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481)
	at com.sun.javafx.tk.Toolkit.getToolkit(Toolkit.java:263)
	at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:291)
	at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:163)
	at com.sun.javafx.application.LauncherImpl.startToolkit(LauncherImpl.java:659)
	at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:679)
	at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:196)
	at java.base/java.lang.Thread.run(Thread.java:840)
Caused by: java.io.InvalidClassException: filter status: REJECTED

On the computer you're trying to launch these on, is there anything in the folder at ~/.ignition/clientlauncher-data/d13n?

@jspecht this looks like it has something to do with the serialization filter, is it documented anywhere how to see what's being rejected and how to add allowed classes?

I don't see something called d13n in my clientlauncher-data folder:

It's possible that launching the client with this additional JVM arg will be enough:

-Dignition.serialFilter.allow=java.rmi.server.RemoteObject

you would configure this in the client launcher.

From a ticket I found, it seems these classes needed to be explicitly allowed for these applications:

Designer (launcher and client):

  • java.rmi.server.RemoteObject

Vision (launcher and client):

  • java.rmi.server.RemoteObject

Perspective Workstation (launcher and client):

  • java.rmi.server.RemoteObject,java.rmi.server.RemoteObject

Kevin beat me to it - those JVM args look correct. Not sure why the Perspective Workstation one has java.rmi.server.RemoteObject twice though, probably just need it once.

So far it's still not connecting, is this the correct location to put it in?

You might need to click the three dots on the existing entry and then "Manage" and add it there - I'm not sure. This looks like the default settings, which may or may not affect existing clients you've already added to the launcher.

There is another key item to note here, the JVM args need to be passed for the launchers at runtime as well as to the clients themselves (the area high lighted in the screenshot you provided). To pass the args to the launchers an ini file needs to be created that captures the JVM args at the time of the application being launched.

To pass the launcher runtime arg you need to create an ini file that is stored in the same folder along side each of their respective exes, which also contains the
-Dignition.serialFilter.allow=java.rmi.server.RemoteObject arg Kevin Noted.

designerlauncher.l4j.ini
perspectiveworkstation.l4j.ini
visionclientlauncher.l4j.ini

4 Likes

Also: JVM arg -Dignition.CompositeClassRejectListFilter.debug=true will enable verbose logging of the class reject filter to help troubleshoot deserialization issues and to determine which classes might need to be added to the allow list.

Is this just for the case where you need to attach to the launcher itself, as opposed to the Designer or Vision Client that was launched?

Do you know what class to set for the ini file?

this is what I have:

I just wanted to clarify and add some additional details on my previous comment as the configuration can be a bit confusing.

The launcher as well as the client both need to have the JVM args in place at launch time.
The .ini file (installed along side the exes) is for the launcher itself. QFTest uses these when opening the launcher, and needs it to be able to perform actions.
The JVM args within the launcher settings (see designer args screen grab) is actually for the designer client. Adding the args will make the client use them at runtime and allow QFTest to perform actions.

A few items of note here:
The ini file only needs the arg in place as this is just to let QFTest interact with the launcher, the rest of the configuration in your screenshot should be done in the launcher settings for the client(see ini location screengrab).

Depending on where you have your launcher exe files installed it could be in a couple of different locations:
Installed for all users Designer location:
C:\Program Files\Inductive Automation\Designer Launcher

Install for me only Designer location:
C:\Users<user>\AppData\Roaming\Inductive Automation\Designer Launcher

Keep in mind this will need to be done for all of the launchers and clients that you intend to use with QFTest (Vision, Designer, Perspective Workstation).

Ensure that your ini files are in the same folder as their respective launcher exe and be sure to use this naming scheme:
designerlauncher.l4j.ini
perspectiveworkstation.l4j.ini
visionclientlauncher.l4j.ini

As Joel mentioned the -Dlauncher.CompositeClassRejectListFilter.debug=true can be useful for finding any additional classes that also need to be allowed.


3 Likes

It's technically both, It needs access to launch the launcher (.ini file) and then the JVM args within the launcher configuration for the client.

4 Likes

Their seems to be something different about doing this for perspective. I completed these steps for the visionclientlauncher and it worked great -- Thank you. But it does not work for perspective, I added the .ini file and also noticed their isn't a spot for jvm arguments within perspective workstation. Thoughts?

Here is the terminal error within QF-Test:

----- perspectiveworkstation:stdout -----
INFO  [CompositeClassRejectListFilter] [2025/05/02 11:05:11]: Initialization performed successfully
INFO  [CompositeClassRejectListFilter] [2025/05/02 11:05:11]: JVM-wide ObjectInputFilter set up successfully
INFO  [CompositeClassRejectListFilter] [2025/05/02 11:05:11]: Platform serialFilter has 88 pattern(s)
INFO  [WorkstationLauncher           ] [2025/05/02 11:05:11]: Set initial logging level to INFO
----- perspectiveworkstation:stderr -----
May 02, 2025 11:05:11 AM com.sun.javafx.application.PlatformImpl startup
WARNING: Unsupported JavaFX configuration: classes were loaded from 'unnamed module @4e41089d'
----- perspectiveworkstation:stdout -----
WARN  [CompositeClassRejectListFilter] [2025/05/02 11:05:15]: Serial class 'com.sun.proxy.$Proxy6' REJECTED by Platform pattern 'com.sun.proxy.**' for security. Set log level to DEBUG to log additional details for future events.
----- perspectiveworkstation:stderr -----
1 (11:05:15.349) JavaFX-Launcher de.qfs.apps.qftest.client.start.ClientStarter.startClient(Object).java.security.PrivilegedAction.run(): (#605) ex: java.rmi.UnmarshalException: error unmarshalling return; nested exception is: 
	java.io.InvalidClassException: filter status: REJECTED
2 (11:05:15.349) JavaFX-Launcher de.qfs.apps.qftest.client.start.ClientStarter.startClient(Object).java.security.PrivilegedAction.run(): (#605) java.rmi.UnmarshalException: error unmarshalling return; nested exception is: 
	java.io.InvalidClassException: filter status: REJECTED
	at java.rmi/sun.rmi.registry.RegistryImpl_Stub.lookup(RegistryImpl_Stub.java:130)
	at java.rmi/java.rmi.Naming.lookup(Naming.java:101)
	at de.qfs.apps.qftest.client.start.b.run(SourceFile:598)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at de.qfs.apps.qftest.client.start.a.a(SourceFile:507)
	at de.qfs.apps.qftest.client.start.SUTWrapper.<init>(SourceFile:205)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
	at de.qfs.apps.qftest.start.Connector.maybeConnect(Connector.java:405)
	at de.qfs.apps.qftest.start.Connector.access$500(Connector.java:81)
	at de.qfs.apps.qftest.start.Connector$5.run(Connector.java:447)
	at de.qfs.apps.qftest.start.Connector.go(Connector.java:475)
	at de.qfs.apps.qftest.start.Connector.access$000(Connector.java:81)
	at de.qfs.apps.qftest.start.Connector$4.run(Connector.java:327)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at de.qfs.apps.qftest.start.Connector.<init>(Connector.java:317)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
	at de.qfs.apps.qftest.qagent.InstanceWaiter$1.run(InstanceWaiter.java:229)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at de.qfs.apps.qftest.qagent.InstanceWaiter.instanceCreated(InstanceWaiter.java:82)
	at de.qfs.apps.qftest.instrument.InstanceTracer.instanceCreated(InstanceTracer.java:206)
	at com.sun.javafx.tk.Toolkit.<init>(Toolkit.java:305)
	at com.sun.javafx.tk.quantum.QuantumToolkit.<init>(QuantumToolkit.java:138)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
	at com.sun.javafx.tk.Toolkit.getToolkit(Toolkit.java:263)
	at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:291)
	at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:163)
	at com.sun.javafx.application.LauncherImpl.startToolkit(LauncherImpl.java:659)
	at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:679)
	at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:196)
	at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: java.io.InvalidClassException: filter status: REJECTED
	at java.base/java.io.ObjectInputStream.filterCheck(ObjectInputStream.java:1369)
	at java.base/java.io.ObjectInputStream.readProxyDesc(ObjectInputStream.java:1951)
	at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1867)
	at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2201)
	at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1687)
	at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:489)
	at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:447)
	at java.rmi/sun.rmi.registry.RegistryImpl_Stub.lookup(RegistryImpl_Stub.java:127)
	... 38 more

For Perspective Workstation, our perspectiveworkstation.l4j.ini file looks like:

# Launch4j runtime config
#-Dlauncher.CompositeClassRejectListFilter.debug=true
-Dignition.serialFilter.allow=java.rmi.server.RemoteObject,com.sun.proxy.**
1 Like

This worked thank you!!

Thank you everyone who contributed on this post. I was able to create these .ini files and add the jvm arguments to launch the program as well as further testing within the clients.

@Kevin.Herron @jspecht @Andrew_Lang @ggross

I appreciate the help, thank you!

-Carter

1 Like