Community Tip - Learn all about the Community Ranking System, a fun gamification element of the PTC Community. X
Hi there,
We recently upgraded to TW 8.4.3 besides that we have had a "hell of time" and not funny way.
I have stumbled upon something that bugs me.
I was use to when service with defined output as string failed an error message from exception appeared in result window.
No there is this weird message "Must have a datashape defined to add rows."
I handle most of my services with try-catch, but not always printing the error part in logs.
This is very difficult to debug.
What is with that, is this by design? Can it be solved?
Solved! Go to Solution.
...Continued...
Now let's consider Infotables and change return value type of ExampleA to INFOTABLE. Two options are possible:
Option One is when you made a typo or simply forgot to declare result variable. You already know what happens in this case -- the service will try to return an undefined, which does not exist natively in Java, this value will be wrapped into a UniqueTag literal object, and everything else will fail, because it will be trying to cast it to Infotable, which it isn't. Take a look at the attached screenshot, I'm sure you've seen it before:
In Option Two you declared result variable, but did not initialize it. Then the service will return... No, you're wrong. An empty Infotable without a data shape, which is essentially a half-cooked Infotable. The latter is a crucial detail, because what happens next is really hard to predict. For example, you can't AddRow to it, and it won't display correctly in Composer. The real problem is that some of those weird side effects will happen down the road (maybe you'll never even realize that you have an issue). We had some sleepless nights debugging and subsequently refactoring such cases.
...All of which brought us to a simple conclusion / best practice: Always initialize your variables as explicitly as possible. If a service is supposed to return an Infotable -- preinitialize result variable with a proper empty Infotable. Same for the strings. If you need to return a number -- return -1 by default, etc. If you really have to return an empty value, then return null explicitly -- this should minimize amount of type conversions that Rhino and ThingWorx will try to do for you. Finally, if there are exception thrown -- don't catch them until strictly necessary, to make sure that those empty values do not proliferate throughout your runtime.
Again, all of above is our own findings based on ThingWorx consulting experience, not validated by PTC R&D. This kind of helps us to have a concise picture of internal mechanics, but in reality it may work completely differently, so don't blame me if that's the case.
/ Constantine
Hi @tcoufal.
We have an internal case for an issue that sounds similar:
The infotable in thing shape user extensions needs a data shape to edit in composer. However, even if you assign a data shape to it, the data shape is not part of the payload returned to Composer when you edit the user. The service should return the datashape.
If the above does not reflect your issue, please provide the specifics. What scenario results in the behavior you're seeing?
Regards.
--Sharon
I found that this error appears when there is a result var declared but has no value.
I made a service containing only:
var result;
in 7.3.4 result window contains nothing but it is green
in 8.4.3 there is this message I dont like
Hello,
This error message is displayed by the Composer, which (I guess) tries to display Data Shape name for returned values and fails because it is undefined. The actual service behavior is the same as in 7.4, it's just the Composer which is different. In other words, your code which uses this service shouldn't be affected. The question of whether or not it is an actual error situation deserves...
A LENGTHY TECHNICAL SIDE NOTE
TL;DR Always initialize your variables.
DISCLAIMER: This is our own hypothesis, which we've never bothered validating with PTC R&D, although we should have better done it if we were smarter.
Handling "empty" values is one of the tricky ThingWorx topics, which developers should be aware of, because it will inevitably hunt you, sooner or later. Take a look a two example services (both returning a STRING for simplicity):
// Service ExampleA, returns STRING
var result; logger.debug(typeof result);
// Service ExampleB, returns NOTHING logger.debug(typeof result);
Both of them produce the same log message "undefined", which is conforming to JavaScript specification. Now let's create the third service, which calls the first two:
logger.debug('A: ' + me.ExampleA()); logger.debug('B: ' + me.ExampleB());
The result is rather unexpected:
A: null B: org.mozilla.javascript.UniqueTag@5cde3530: NOT_FOUND
There's a long explaination behind this, which goes to the fact that although from the JavaScript perspective both examples are equivalent, Rhino (the scripting engine that runs your JavaScript on the server side) handles them differently, because even though the variable "result" is not defined in the first case, it is still declared, and thus exists in the Script Context as an instance of org.mozilla.javascript.Undefined. I guess ThingWorx could have handled those two cases similarty to comply with JavaScript semantics, but it doesn't. It's not clear to me why, but I guess they had some good reason for it. Anyhow, this subtle difference has dramatic consequences.
If you want to understand that cryptic "org.mozilla.javascript.UniqueTag@5cde3530: NOT_FOUND" message, here's another explaination. In JavaScript if you don't initialize a variable, then basically "result === undefined". Unfortunately there's no direct equvalent of undefined in Java, it only supports null values, but those are already mapped to JavaScript null. So the best thing that Rhino can do in this case is to use a UniqueTag object called "NOT_FOUND" (basically a string literal), which is neither an empty string, nor null in Java. To make matters worse, once it leaves the context of the original script (e.g. when you are calling service A from service B), it stops being falsy! Again, very easy to test:
// Service ExampleA, returns STRING
if (typeof result != 'undefined') {
logger.debug("A: There's something");
} else {
logger.debug("A: There's nothing");
}
// Service ExampleB, returns NOTHING
var resultOfA = me.ExampleA(); if (resultOfA) {
logger.debug("B: There's something");
} else {
logger.debug("B: There's nothing");
}
If you execute ExampleB, you'll get this in the script log:
A: There's nothing B: There's something
By the way, it works more intuitively if you add "var result;" to the ExampleA. Then the service outputs a proper null value, which Java wrapper around Rhino doesn't try to cast to anything else than null, and carries forward just as a null, so it propagates into ExampleB, where it is considered a falsy, outputting "B: There's nothing".
Guess what is typeof resultOfA. Indeed, it's a string, and if you try to serialize this value somehow (save it in a data table, output as XML or JSON, etc.) you'll get a legit string literal "org.mozilla......", which is obviously neither undefined, nor null nor falsy...
...Continued in the next comment...
...Continued...
Now let's consider Infotables and change return value type of ExampleA to INFOTABLE. Two options are possible:
Option One is when you made a typo or simply forgot to declare result variable. You already know what happens in this case -- the service will try to return an undefined, which does not exist natively in Java, this value will be wrapped into a UniqueTag literal object, and everything else will fail, because it will be trying to cast it to Infotable, which it isn't. Take a look at the attached screenshot, I'm sure you've seen it before:
In Option Two you declared result variable, but did not initialize it. Then the service will return... No, you're wrong. An empty Infotable without a data shape, which is essentially a half-cooked Infotable. The latter is a crucial detail, because what happens next is really hard to predict. For example, you can't AddRow to it, and it won't display correctly in Composer. The real problem is that some of those weird side effects will happen down the road (maybe you'll never even realize that you have an issue). We had some sleepless nights debugging and subsequently refactoring such cases.
...All of which brought us to a simple conclusion / best practice: Always initialize your variables as explicitly as possible. If a service is supposed to return an Infotable -- preinitialize result variable with a proper empty Infotable. Same for the strings. If you need to return a number -- return -1 by default, etc. If you really have to return an empty value, then return null explicitly -- this should minimize amount of type conversions that Rhino and ThingWorx will try to do for you. Finally, if there are exception thrown -- don't catch them until strictly necessary, to make sure that those empty values do not proliferate throughout your runtime.
Again, all of above is our own findings based on ThingWorx consulting experience, not validated by PTC R&D. This kind of helps us to have a concise picture of internal mechanics, but in reality it may work completely differently, so don't blame me if that's the case.
/ Constantine
Hi @tcoufal.
If you feel one of the previous posts has answered your question, please mark the appropriate one as the Accepted Solution for the benefit of others with the same question.
Regards.
--Sharon