I have seen a few references to this exception before but I believe in the context of the base product. I am working on improving a module we developed a while back to clean up exceptions, improve comments, and remove code that was derived from playing and understanding the SDK. In that effort, I am really struggling with a ConcurrentModificationException that keeps cropping up in my tagChanged hook.
Here is the full exception that I am getting. The log has highlighted the “at com.inductiveautomation.ignition.gateway.tags.subscriptions.ProviderSubscriptionManagerImpl$TagChangePublish.run(ProviderSubscriptionManagerImpl.java:1138)” line.
java.util.ConcurrentModificationException: null
at java.base/java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.base/java.util.ArrayList$Itr.next(Unknown Source)
at com.jnjdodev.ignition.modules.websocket.UtilSingleton$1.tagChanged(UtilSingleton.java:78)
at com.inductiveautomation.ignition.gateway.tags.subscriptions.ProviderSubscriptionManagerImpl$TagChangePublish.run(ProviderSubscriptionManagerImpl.java:1138)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)
Below is the code for the tagChanged hook we built. I’m very sure that more information might be needed to help find the issue, but I’m not sure what others might find relevant so I thought we would start at the stacktrace line indicated in the error
public class UtilSingleton {
private static UtilSingleton single_instance = null;
private GatewayContext context;
private ArrayList<WebSocketHandler> socketHandle;
private Logger logger;
private Server server;
private List<TagPath> tagSubscriptions;
private ArrayList<TreeNode> tagSubscriptionsTree;
private com.inductiveautomation.ignition.common.tags.model.event.TagChangeListener tcl;
private WebSocketSettingsRecord settings = null;
private Thread th = null;
private UtilSingleton() {
socketHandle = new ArrayList<WebSocketHandler>();
setTagSubscriptions(new ArrayList<TagPath>());
tagSubscriptionsTree = new ArrayList<TreeNode>();
// A reference of the TagChangeListener thread
tcl = new com.inductiveautomation.ignition.common.tags.model.event.TagChangeListener() {
@Override
public void tagChanged(com.inductiveautomation.ignition.common.tags.model.event.TagChangeEvent e) throws InvalidListenerException {
LogInfo("Tag Changed Fired for Tag: " + e.getTagPath() + " Value:"+ e.getValue().toString(),false);
try {
Gson g = new Gson();
// Gets the value of this.socketHandle from the parent class
if (getSocketHandle() != null) {
// Creates an object structure that will be converted to JSON for my apps to consume
ReportingTag rt = new ReportingTag(e.getValue(), e.getTagPath());
// Loop our socket clients to send them messages
for (WebSocketHandler webSocketHandler : getSocketHandle()) {
// Evaluate if a client has a specific subscription to a tagpath (specific PLC) or wants all updates
if (webSocketHandler.subscriptionPath != "") {
if (e.getTagPath().toString().toUpperCase().startsWith(webSocketHandler.subscriptionPath.toUpperCase())) {
LogInfo("Sending Explicit Subscription Tag Change to " + webSocketHandler.hostname + " (" + webSocketHandler.ipAddy + ")", false);
webSocketHandler.SendMessage(g.toJson(rt));
}
// Send all tag updates
} else {
LogInfo("Sending Tag Change to " + webSocketHandler.hostname + " (" + webSocketHandler.ipAddy + ")", false);
webSocketHandler.SendMessage(g.toJson(rt));
}
}
}
} catch (NullPointerException ex) {
// STUB Still trying to understand why and when this exception fires
LogError("Null Pointer in tag change event", ex);
} catch (Exception ex) {
LogError("Error sending tag change event", ex);
}
}
};
}
I am suspecting that my loop is taking long enough that the same tag change is firing in that time and that’s what is causing the exception.
My plan, even if it doesn’t fix this exception, is to move this looping and message sending logic to a blocking collection in a different thread so that the tag change from ignition can fire on through and I can send messages, in order, on the side.
Curious on the thoughts from others what I should maybe look for or do as I transition this.