i tried calling getFullPath from a NodeDescription object, but it returns null. Does anyone know if I am doing something wrong here?
Results<NodeDescription> browseResults = context.getTagManager().browseAsync(tagPath, browseFilter, securityContext).get();
Collection<NodeDescription> tagDescriptions = browseResults.getResults();
if (tagDescriptions != null) {
for (NodeDescription tagDesc : tagDescriptions) {
// this line will throw null pointer error
logger.log(tagDesc.getFullPath().toString());
Can you provide the exact stack trace? What are you ultimately trying to do?
At a glance your code looks okay. Where are you running it?
Sure thing, at the bottom is the stack trace. Ultimately, I am adding a feature to the ignition NodeRed module to allow browsing tags with a browseFilter through http post call. I am running it inside an Ignition Gateway (v8.1.43). And below is the actual code that produces this exception.
// Create browse filter
BrowseFilter browseFilter = createBrowseFilter(browseFilterJson);
Results<NodeDescription> browseResults = context.getTagManager().browseAsync(tagPath, browseFilter, securityContext).get();
Collection<NodeDescription> tagDescriptions = browseResults.getResults();
if (tagDescriptions != null) {
for (NodeDescription tagDesc : tagDescriptions) {
JSONObject tagObject = new JSONObject();
tagObject.put("name", tagDesc.getName());
// todo: tagDesc.getFullPath() returns null for some tags, need to handle this case
tagObject.put("tagPath", tagDesc.getFullPath().toString());
and here is the stack trace
java.lang.NullPointerException: Cannot invoke "Object.toString()" because the return value of "com.inductiveautomation.ignition.common.tags.browsing.NodeDescription.getFullPath()" is null
at org.imdc.nodered.servlet.TagCommandHandler.browseTags(TagCommandHandler.java:120)
at org.imdc.nodered.servlet.TagCommandHandler.handleTagBrowse(TagCommandHandler.java:89)
at org.imdc.nodered.servlet.NodeREDServlet.doPost(NodeREDServlet.java:166)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:523)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:590)
at com.inductiveautomation.ignition.gateway.bootstrap.MapServlet.service(MapServlet.java:86)
at org.eclipse.jetty.servlet.ServletHolder$NotAsync.service(ServletHolder.java:1410)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:764)
at org.eclipse.jetty.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1665)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:527)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:131)
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:598)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:223)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1580)
at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1384)
at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:176)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:484)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1553)
at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:174)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1306)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
at com.inductiveautomation.catapult.handlers.RemoteHostNameLookupHandler.handle(RemoteHostNameLookupHandler.java:121)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
at org.eclipse.jetty.rewrite.handler.RewriteHandler.handle(RewriteHandler.java:301)
at org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:51)
at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:141)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
at org.eclipse.jetty.server.Server.handle(Server.java:563)
at org.eclipse.jetty.server.HttpChannel$RequestDispatchable.dispatch(HttpChannel.java:1598)
at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:753)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:501)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:287)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:314)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)
at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint.onFillable(SslConnection.java:558)
at org.eclipse.jetty.io.ssl.SslConnection.onFillable(SslConnection.java:379)
at org.eclipse.jetty.io.ssl.SslConnection$2.succeeded(SslConnection.java:146)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)
at org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)
at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:421)
at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:390)
at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:277)
at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.run(AdaptiveExecutionStrategy.java:199)
at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:411)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:969)
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1194)
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1149)
What's the tagpath you're passing in? Is it fully qualified, including a provider name?
my apology, i usually get a notification when someone replies, but this time I didn't.
I am not sure if it is, but here is the line of code that constructs the tagPath and it looks like would have a provider.
TagPath tagPath = TagPathParser.parseSafe(defaultTagProvider, tagPathStr);
should I do it in a different way? this was the original code from the module repo

This particular area is very complicated -- browse at the tag manager level gets indirected into particular tag providers, then depending on the tag provider gets indirected into a particular BrowsableNode
, and then that particular BrowsableNode
will have different behavior depending on exactly what type of tag it is (i.e. udt definition vs instance vs folder vs regular tag).
So it seems like it might be entirely legitimate to have a null full path depending on what path you're actually browsing, but it's also possible there's something else wrong in this example that I'm not seeing. Unfortunately I'm not versed enough in the tag system to say for sure.
gotcha, I figured I would just not rely on getFullPath and build the fullPath based on tagName and tagPath instead. But then it would not produce the correct fullPath with recursive browseFilter, so I ended up having to write my own browse function to support recursive browsing while getting the full path.
I am sharing my working function below. it works, but maybe there is a better way to do this.
private Collection<NodeBrowseInfo> browseTagsHelper(TagPath tagPath, SecurityContext securityContext, BrowseFilter browseFilter) throws ExecutionException, InterruptedException {
boolean recursive = browseFilter.isRecursive();
Results<NodeDescription> browseResults = context.getTagManager().browseAsync(tagPath, BrowseFilter.NONE, securityContext).get();
Collection<NodeDescription> nodeDescriptions = browseResults.getResults();
if (nodeDescriptions == null || nodeDescriptions.isEmpty()) return null;
Collection<NodeBrowseInfo> nodeBrowseInfos = new ArrayList<>();
for(NodeDescription node: nodeDescriptions) {
TagPath fullPath = tagPath.getChildPath(node.getName());
if (browseFilter.checkProperties(node) && browseFilter.checkNameFilters(fullPath)) {
// If the node matches the filter, add it to the results
nodeBrowseInfos.add(new NodeBrowseInfo(fullPath, node));
}
if (recursive && node.hasChildren() && node.getDataType() != DataType.Document) {
// If the node is a folder and recursion is enabled, browse it recursively
Collection<NodeBrowseInfo> childResults = browseTagsHelper(fullPath, securityContext, browseFilter);
if (childResults != null) {
nodeBrowseInfos.addAll(childResults);
}
}
}
return nodeBrowseInfos;
}