Stackoverflow calling metaClass method on Map - java

This piece of code is causing a stackOverflow exception:
ISerializer serializer = buildSerializer(TestDataProvider.getAuthor());
ASObject result = (ASObject) serializer.serialize();
assert result.isNotLazyProxy
The StackOverflow is being thrown on this line: assert result.isNotLazyProxy. Note, the isNotLazyProxy method never actually gets called.
isNotLazyProxy is a extension method (what are these called in groovy?) defined as follows:
/**
* Asserts that this ASObject is not a lazy loaded proxy,
* ie - that all of it's properties' values have been included
*/
ASObject.metaClass.isNotLazyProxy = { ->
assert delegate[HibernateProxyConstants.PROXYINITIALIZED] == true
return true;
}
However, setting a breakpoint on the first line of that closure shows that it never gets called.
Instead, there's a StackOverflow thrown:
java.lang.StackOverflowError
at java.lang.System.arraycopy(Native Method)
at java.lang.String.getChars(String.java:855)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:391)
at java.lang.StringBuffer.append(StringBuffer.java:224)
at java.lang.StringBuffer.<init>(StringBuffer.java:104)
at org.codehaus.groovy.runtime.InvokerHelper.formatMap(InvokerHelper.java:557)
at org.codehaus.groovy.runtime.InvokerHelper.format(InvokerHelper.java:530)
at org.codehaus.groovy.runtime.InvokerHelper.formatList(InvokerHelper.java:602)
at org.codehaus.groovy.runtime.InvokerHelper.format(InvokerHelper.java:527)
at org.codehaus.groovy.runtime.InvokerHelper.formatMap(InvokerHelper.java:575)
snip
I'm not sure if it's relevant, but ASObject is a subclass of a Map, and it's contents may have properties that refer to other keys within itself.
I would've have thought it was relevant, except the StackOverflow appears to indicate that groovy is traversing the members of the map.
What's going on? Why is this stackoverflow occurring?

assert result.isNotLazyProxy is probably not doing what you want to do.
In groovy map.somehing is translated to map.get(something). See http://groovy.codehaus.org/JN1035-Maps :
assert map2.class == null
//field syntax always refers to value of key, even if it doesn't exist
//use getClass() instead of class for maps...
assert map2.getClass() == LinkedHashMap //the kind of Map being used
So use assert result.isNotLazyProxy().
Of course result.isNotLazyProxy should return null in your case, and the assert result.isNotLazyProxy assertion should fail. When this assertion fails groovy will display an assertion error, and the map. In your case formatting the map fails for some reason.
Reason it fails:
It is a known bug, see example. As I see, it has nothing to do with ASObject, as it uses no lists.

At first glance, it looks like something else is causing the endless recursion...
Does ASObject have any other metaClass methods? Especially those overriding the getAt( key ) method of Map
I came up with this quick test script, and it seems to work fine as I'd expect (and I think it does the same as what you say you're doing)
class ASObject {
#Delegate Map map = [ 'PROXYINITIALIZED' : true ]
}
ASObject.metaClass.isNotLazyProxy = { ->
assert delegate[ 'PROXYINITIALIZED' ] == true
return true
}
assert new ASObject().isNotLazyProxy()

Related

Why mockito will trigger the verify twice?

I notice this problem during the following test:
verify(mockedObject).functionCall(argThat(inputStream -> {
final String content = ... // read the inputStream
assertEquals(expectedContent, content);
return true;
}));
It will actually fail although the assertEquals assertion is true. I debug the test, find that the lambda function is reached twice, and at the second time, the cursor of the stream is at the end of the stream. That's why it fails.
So I have to reset the stream first:
verify(mockedObject).functionCall(argThat(inputStream -> {
inputStream.reset();
final String content = ... // read the inputStream
assertEquals(expectedContent, content);
return true;
}));
The question is, why the lambda is triggered twice? Is this by design? Does it have a document?
Mockito version: 2.22
Junit version: 5.6.0
Java version: 1.8
Update
The method is called exactly once, and the inputs of two lambda calls are exactly the same input. Actually, they are the same object. The only thing I have to do is to reset the stream, as it has been exhausted by the first lambda call.
I wouldn't say it's "by design", rather that it's what the current implementation does. The Mockito Times class which performs your assertions has the following method (I'm on a pretty recent version so YMMV):
public void verify(VerificationData data) {
List<Invocation> invocations = data.getAllInvocations();
MatchableInvocation wanted = data.getTarget();
if (wantedCount > 0) {
checkMissingInvocation(data.getAllInvocations(), data.getTarget());
}
checkNumberOfInvocations(invocations, wanted, wantedCount);
}
Both checkMissingInvocation and checkNumberOfInvocations perform independent filtering on the list of all invocations to retain the relevant ones, so any matcher you declare ends up being executed twice for each invocation. It's actually exactly the same call:
List<Invocation> actualInvocations = findInvocations(invocations, wanted);
Maybe the filtered list could be cached, but the point is that unless otherwise specified in the documentation, you cannot assume that the function you supply will be executed only once. Also, predicate functions are generally expected to be free of side-effects.

Play 2.5: Form validation - on error, value of form is "Optional.empty"

I am developing an application with Play 2.5. Models and Form data are separate classes, so I have a class "Page" and "PageForm".
In PageForm is a method "validate()" which returns null if there was no error or a List if the validation failed:
public List<ValidationError> validate() {
List<ValidationError> errors = new ArrayList<>();
Page checkForDuplicatePage = PageRepository.getInstance().getByName(name);
if(checkForDuplicatePage != null && checkForDuplicatePage.id != id) {
errors.add(new ValidationError("name", "The name is already in use by another page"));
}
// ...
return errors.isEmpty() ? null : errors;
}
In my controller I call:
Form<PageForm> form = formFactory(PageForm.class).bindFromRequest();
This works really well if the data in the form is correct. However, if validate() finds an error (and it really doesn't matter what kind, even a return new ArrayList<>() triggers this), the "value" attribute of my form is Optional.empty. The "data" attribute actually has all the data passed to the form.
This means I can't use the form to pass it to my view, which should display the data with error messages. Instead I get a [CompletionException: java.util.NoSuchElementException: No value present]. Sometimes (I haven't figured out why that happens yet) it also says [CompletionException: java.util.NoSuchElementException: None.get].
I compared my code with other projects and the official docs, but they all seem to be doing what I have here.
I use Scala Play rather than Java, so YMMV. But to me, I don't think that validate should return null at all. It should return the empty ArrayList if there are no errors. I suspect that this will eliminate the None.get error message. I'm not sure how much I can help, though, because I don't really understand very well what your code is intended to do. For example, the sentence
However, if validate() finds an error (and it really doesn't matter what kind, even a return new ArrayList<>() triggers this)
seems kind of ambiguous to me. Where is the return new ArrayList<>() call that triggers the error?

IllegalArgumentException: Invalid conditional statement inside expectation block

I have a problem with an Expectations block I have written in my test case:
new Expectations() {
{
mFindHandlerMock.findAll((Model) any, (Set<Id>) any, false);
if (!pWithRealData) {
result = Collections.emptySet();
} else {
result = pAllData;
}
times = 1;
Deencapsulation.invoke(mDb, "readSqlQuery", withAny(String.class));
result = "select * from realdata";
times = 1;
}
};
the test case crashes with:
java.lang.IllegalArgumentException: Invalid conditional statement inside expectation block
exactly here:
if (!pWithRealData) {
it's just a simple boolean that is false in this case.
I have absolutly no clue why the exception happens.
I already searched with google but found nothing helpful.
Could you help me?
From the JMockit release notes for version 1.14:
Enhancement: Conditionals and loops will now trigger an exception when found inside an expectation recording block, to prevent API misuse and to encourage simpler tests. See issue #97.
The GitHub issues related to this:
https://github.com/jmockit/jmockit1/issues/97
https://github.com/jmockit/jmockit1/issues/123
In the one issue they state that:
Yes, and this is as intended, to avoid tests getting too complicated when recording expectations. A full test was not shown, but it looks to me that recording the specific expectations directly would be better in this case.
In the JMockit source you can see which other types of conditionals and loops will throw that exception.
In short, from JMockit 1.14 onwards you are not allowed to have conditionals (such as if statements) and loops in the Expectation block.

Java Enum Returns Null

I am currently creating a plugin for Minecraft using the SpigotAPI. Reason I'm posting this here is because this I believe is a Java error. I am creating a duels plugin where inside my code it'll loop through an enum, and see if it's a specific type. The first time using it around it properly works, no problems. But when I try it for a second time without restarting my plugin/program/code it'll return the enum as null. Here is the code, is there a fix to this?
public DuelArenas[] getArenasWithType(DuelTypes type) {
String suffix = "_NORMAL";
List<DuelArenas> arenasAsList = new ArrayList<>();
switch (type) {
case NORMAL:
suffix = "_NORMAL";
break;
}
for (DuelArenas arena : duelArenaStatus.keySet()) {
if (arena.toString().endsWith(suffix)) {
arenasAsList.add(arena);
}
}
DuelArenas[] arenas = new DuelArenas[arenasAsList.size()];
return arenasAsList.toArray(arenas);
}
Stacktrace:
Caused by: java.lang.NullPointerException
at me.nick.acore.duels.DuelsAPI.getArenasWithType(DuelsAPI.java:97) ~[?:?]
And yes I've checked to see if the enum was null, and it was in fact null. Also line 97 is
if (arena.toString().endsWith(suffix)) {
And finally here is the DuelArena class
public enum DuelArenas {
ARENA_1_NORMAL, ARENA_2_NORMAL, ARENA_3_NORMAL, ARENA_4_NORMAL, ARENA_5_NORMAL,
ARENA_6_NORMAL, ARENA_7_NORMAL, ARENA_8_NORMAL, ARENA_9_NORMAL, ARENA_10_NORMAL,
ARENA_11_NORMAL, ARENA_12_NORMAL }
Thanks!
Your problem is that you cannot directly convert your custom DuelArenas class to a string. However when you are comparing to see if a .toString() ends with suffix, I feel that something is also going wrong. You would only ever use .toString to convert things like numbers to strings, and if your are converting a number to a string there is no way it will end in _NORMAL.
So if you want me to troubleshoot further please post your DuelArenas class, but until then my best guess is that when you do arena.toString you are looking to pull some sort of value from that class that is stored in it, and to do this you would do arena.variableInsideArenaName and work with that.
EDIT:
After seeing the class scratch that, the error in going to be somewhere in this line DuelArenas arena : duelArenaStatus.keySet()

Optimized way to populate data in a object

I am working on a solution where I need to populate certain fields in a DataObject, though fields are predefined but source from where I need to populate this data is not in my control and I can not do any modification or changes.
This is a structure of my Source Object
SourceObject
-Collection<Features>
-Collection<FeatureData>
Attribute Name is defined in SourceObject which will help me to decided if I want that attribute value or not (There are many attributes (Framework Provided + Custom one)) and Value is being provided from Collection<FeatureData>
for(SourceData sourceData : productData.getSourceData())
{
if(sourceData.getCode().equalsIgnoreCase("classification"))
{
if(CollectionUtils.isNotEmpty(sourceData.getFeatures()))
{
for(FeatureData featureData : sourceData.getFeatures()){
if(CollectionUtils.isNotEmpty(featureData.getFeatureValues())){
if(featureData .getCode().contains("customValue1")){
for(FeatureValueData featureDataValue: featureData.getFeatureValues()){
productData.setPower(featureDataValue.getValue()));
break;
}
}
}
break;
}
}
}
}
But that means I have to do this (Check and Fill) for all my custom attributes. Is there way I can handle it in good way ?.
Please do not pay much attention to syntax or any potential NPE etc, as I will going to take care of those issues
From what I can comprehend from your code is that you are trying to find the very first sourceData whose code matches the "classification" value and also whose features collection isn't empty.
Then once such sourceData is found, you are trying to find the very first feature from the sourceData's features collection whose code contains the "customValue1" in it and whose featureValueData collection isn't empty.
Once such feature is found, you are effectively setting the productData's power as the value that is held by the very first featureValueData of the featureValueData collection of the feature.
Such code can be rewritten as follows:
// Start
SourceData sourceData = findFirstValidSource(productData.getSourceData());
if (sourceData == null) // Can remove this check if sure that at least one valid source data will always exist.
{
return;
}
FeatureData feature = findFirstValidFeature(sourceData.getFeatures());
if (feature == null) // Can also remove this check if sure that at least one valid feature data will always exist.
{
return;
}
FeatureValueData featureValueData = feature.getFeatureValues().iterator().next();
productData.setPower(featureValueData.getValue());
// End
The findFirstValidSource() method is implemented as follows:
private SourceData findFirstValidSource(Collection<SourceData> sources)
{
for (SourceData source : sources)
{
if (source.getCode().equalsIgnoreCase("classification") && CollectionUtils.isNotEmpty(source.getFeatures()))
{
return source;
}
}
return null;
}
The findFirstValidFeature() method is implemented as follows:
private FeatureData findFirstValidFeature(Collection<FeatureData> features)
{
for (FeatureData feature : features)
{
if (feature.getCode().contains("customValue1") && CollectionUtils.isNotEmpty(feature.getFeatureValues()))
{
return feature;
}
}
return null;
}
The above code will also do the exact same thing that your code is doing, except that its more readable and understandable now. The code can save some more processing if you make your getFeatureValues() method return a List instead of a Collection, as then it can grab the very first element through indexing (if implementation is ArrayList based) or by getting the first element (if implementation is LinkedList based), which will take constant time.
Not that the iterator().next() takes any more time, it just is useless to create an iterator in this case when there's no requirement of it as I did in the line:
FeatureValueData featureValueData = feature.getFeatureValues().iterator().next();
If getFeatureValues() would have returned a List, we could have written:
FeatureValueData featureValueData = feature.getFeatureValues().get(0);
instead.
Furthermore, the code can be made more compact by use of Lambda Expressions and Streams which are introduced in JDK 8. If you have no issues using the new features of Java 8, I can update my answer to incorporate a terser solution as well.
Let me know if my answer helped or if your expectations are still not met.

Categories