-
Notifications
You must be signed in to change notification settings - Fork 832
fix logMissingProperties (WW-4999) #358
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
3ac6835
0999fba
d4dd338
9e01fbd
a50af87
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -217,13 +217,14 @@ public Object callMethod(Map context, Object target, String name, Object[] objec | |||||
return null; | ||||||
} | ||||||
|
||||||
Throwable reason = null; | ||||||
Class[] argTypes = getArgTypes(objects); | ||||||
for (Object o : root) { | ||||||
if (o == null) { | ||||||
continue; | ||||||
} | ||||||
|
||||||
Class clazz = o.getClass(); | ||||||
Class[] argTypes = getArgTypes(objects); | ||||||
|
||||||
MethodCall mc = null; | ||||||
|
||||||
|
@@ -233,24 +234,27 @@ public Object callMethod(Map context, Object target, String name, Object[] objec | |||||
|
||||||
if ((argTypes == null) || !invalidMethods.containsKey(mc)) { | ||||||
try { | ||||||
Object value = OgnlRuntime.callMethod((OgnlContext) context, o, name, objects); | ||||||
return OgnlRuntime.callMethod((OgnlContext) context, o, name, objects); | ||||||
} catch (OgnlException e) { | ||||||
reason = e.getReason(); | ||||||
|
||||||
if (value != null) { | ||||||
return value; | ||||||
if (reason != null && !(reason instanceof NoSuchMethodException)) { | ||||||
// method has found but thrown an exception | ||||||
break; | ||||||
} | ||||||
} catch (OgnlException e) { | ||||||
// try the next one | ||||||
Throwable reason = e.getReason(); | ||||||
|
||||||
if (!context.containsKey(OgnlValueStack.THROW_EXCEPTION_ON_FAILURE) && (mc != null) && (reason != null) && (reason.getClass() == NoSuchMethodException.class)) { | ||||||
if ((mc != null) && (reason != null)) { | ||||||
invalidMethods.put(mc, Boolean.TRUE); | ||||||
} else if (reason != null) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it certain that dropping the "if (reason != null)" check as part of the decision whether to throw the exception or not won't cause an issue ? It looks like the old code would "skip" throwing an Exception in that case, no matter what There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As @qqu0127 mentioned, it seems in both I realized that this is an old issue, please see this test :
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, you are right.
And
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm still not sure if we should remove There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should because with that following test fails. I tested and even Struts 2.5 (5/5/2016) with no change also fails on this test:
but this is and was a wrong behavior. |
||||||
throw new MethodFailedException(o, name, e.getReason()); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Was the old logic intentionally limiting the loop to 1 iteration (unless the exception reason was Will "walking the whole valuestack" potentially introduce a different issue (unexpectedly) ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In case of
No, it is already current behavior in case of |
||||||
} | ||||||
// continue and try the next one | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
if (context.containsKey(OgnlValueStack.THROW_EXCEPTION_ON_FAILURE)) { | ||||||
throw new MethodFailedException(target, name, reason); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems like previously this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, previously it was mixed up both cases also so it is same as previous in this behavior. The only difference for it is: Now it is outside of the loop. Being inside of the loop is a bug. I discovered it in #357 - For example
I think no. AFAIK it must try on all of objects in stack. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks ok to me, it makes sense to keep the mixed up behavior. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now, it doesn't break the loop but previously it did, and I thought it was an issue when I just found all usages of |
||||||
} | ||||||
|
||||||
return null; | ||||||
} | ||||||
|
||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,9 +34,16 @@ | |
|
||
import java.io.*; | ||
import java.math.BigDecimal; | ||
import java.util.ArrayList; | ||
import java.util.HashMap; | ||
import java.util.LinkedHashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
import org.apache.logging.log4j.LogManager; | ||
import org.apache.logging.log4j.core.LogEvent; | ||
import org.apache.logging.log4j.core.Logger; | ||
import org.apache.logging.log4j.core.appender.AbstractAppender; | ||
import org.apache.struts2.StrutsConstants; | ||
|
||
|
||
|
@@ -238,6 +245,95 @@ public void testFailOnMissingProperty() { | |
} | ||
} | ||
|
||
/** | ||
* monitors the resolution of WW-4999 | ||
* @since 2.5.21 | ||
*/ | ||
public void testLogMissingProperties() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 fixed |
||
testLogMissingProperties(true); | ||
testLogMissingProperties(false); | ||
} | ||
|
||
private void testLogMissingProperties(boolean logMissingProperties) { | ||
OgnlValueStack vs = createValueStack(); | ||
vs.setLogMissingProperties("" + logMissingProperties); | ||
|
||
Dog dog = new Dog(); | ||
vs.push(dog); | ||
|
||
TestAppender testAppender = new TestAppender(); | ||
Logger logger = (Logger) LogManager.getLogger(OgnlValueStack.class); | ||
logger.addAppender(testAppender); | ||
testAppender.start(); | ||
|
||
try { | ||
vs.setValue("missingProp1", "missingProp1Value", false); | ||
vs.findValue("missingProp2", false); | ||
vs.findValue("missingProp3", Integer.class, false); | ||
|
||
if (logMissingProperties) { | ||
assertEquals(3, testAppender.logEvents.size()); | ||
assertEquals("Error setting value [missingProp1Value] with expression [missingProp1]", | ||
testAppender.logEvents.get(0).getMessage().getFormattedMessage()); | ||
assertEquals("Could not find property [missingProp2]!", | ||
testAppender.logEvents.get(1).getMessage().getFormattedMessage()); | ||
assertEquals("Could not find property [missingProp3]!", | ||
testAppender.logEvents.get(2).getMessage().getFormattedMessage()); | ||
} else { | ||
assertEquals(0, testAppender.logEvents.size()); | ||
} | ||
} finally { | ||
testAppender.stop(); | ||
logger.removeAppender(testAppender); | ||
} | ||
} | ||
|
||
/** | ||
* tests the correctness of distinguishing between user exception and NoSuchMethodException | ||
* @since 2.5.21 | ||
*/ | ||
public void testNotLogUserExceptionsAsMissingProperties() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 fixed |
||
OgnlValueStack vs = createValueStack(); | ||
vs.setLogMissingProperties("true"); | ||
|
||
Dog dog = new Dog(); | ||
vs.push(dog); | ||
|
||
TestAppender testAppender = new TestAppender(); | ||
Logger logger = (Logger) LogManager.getLogger(OgnlValueStack.class); | ||
logger.addAppender(testAppender); | ||
testAppender.start(); | ||
|
||
try { | ||
vs.setValue("exception", "exceptionValue", false); | ||
vs.findValue("exception", false); | ||
vs.findValue("exception", String.class, false); | ||
vs.findValue("getException()", false); | ||
vs.findValue("getException()", String.class, false); | ||
vs.findValue("bite", false); | ||
vs.findValue("bite", void.class, false); | ||
vs.findValue("getBite()", false); | ||
vs.findValue("getBite()", void.class, false); | ||
|
||
vs.setLogMissingProperties("false"); | ||
|
||
vs.setValue("exception", "exceptionValue", false); | ||
vs.findValue("exception", false); | ||
vs.findValue("exception", String.class, false); | ||
vs.findValue("getException()", false); | ||
vs.findValue("getException()", String.class, false); | ||
vs.findValue("bite", false); | ||
vs.findValue("bite", void.class, false); | ||
vs.findValue("getBite()", false); | ||
vs.findValue("getBite()", void.class, false); | ||
|
||
assertEquals(0, testAppender.logEvents.size()); | ||
} finally { | ||
testAppender.stop(); | ||
logger.removeAppender(testAppender); | ||
} | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @yasserzamani , do you think adding equivalent tests They could set As it stands the changes LGTM, but I think those two additional tests might be useful to catch a future change that accidentally causes the opposite behaviour (i.e. something gets logged when There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hello @yasserzamani . Thanks for responding to the feedback and your excellent work on this PR. 👍 . There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 fixed |
||
public void testFailOnMissingMethod() { | ||
OgnlValueStack vs = createValueStack(); | ||
|
||
|
@@ -879,6 +975,49 @@ public void testStatics() { | |
assertNull(vs.findValue("@com.nothing.here.Nothing@BLAH")); | ||
} | ||
|
||
/** | ||
* Fails on 2.5.20 and earlier - tested on 2.5 (5/5/2016) and failed | ||
* @since 2.5.21 | ||
*/ | ||
public void testNotThrowExceptionOnTopMissingProperty() { | ||
OgnlValueStack vs = createValueStack(); | ||
|
||
Dog dog = new Dog(); | ||
dog.setName("Rover"); | ||
vs.push(dog); | ||
|
||
Cat cat = new Cat(); | ||
vs.push(cat); | ||
|
||
vs.setValue("age", 12, true); | ||
|
||
assertEquals(12, vs.findValue("age", true)); | ||
assertEquals(12, vs.findValue("age", Integer.class, true)); | ||
assertEquals(12, vs.findValue("getAge()", true)); | ||
assertEquals(12, vs.findValue("getAge()", Integer.class, true)); | ||
} | ||
|
||
/** | ||
* Fails on 2.5.20 and earlier - tested on 2.5 (5/5/2016) and failed | ||
* @since 2.5.21 | ||
*/ | ||
public void testNotSkipUserReturnedNullValues() { | ||
OgnlValueStack vs = createValueStack(); | ||
|
||
Dog dog = new Dog(); | ||
dog.setName("Rover"); | ||
vs.push(dog); | ||
|
||
Cat cat = new Cat(); | ||
vs.push(cat); | ||
|
||
// should not skip returned null values from cat.name | ||
assertNull(vs.findValue("name", true)); | ||
assertNull(vs.findValue("name", String.class, true)); | ||
assertNull(vs.findValue("getName()", true)); | ||
assertNull(vs.findValue("getName()", String.class, true)); | ||
} | ||
|
||
public void testTop() { | ||
OgnlValueStack vs = createValueStack(); | ||
|
||
|
@@ -1293,6 +1432,19 @@ public void setDisplayName(String displayName) { | |
this.displayName = displayName; | ||
} | ||
} | ||
|
||
class TestAppender extends AbstractAppender { | ||
List<LogEvent> logEvents = new ArrayList<>(); | ||
|
||
TestAppender() { | ||
super("TestAppender", null, null, false, null); | ||
} | ||
|
||
@Override | ||
public void append(LogEvent logEvent) { | ||
logEvents.add(logEvent); | ||
} | ||
} | ||
} | ||
|
||
enum MyNumbers { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Oops ... forgot to include this comment in the review).
Assuming the loop logic will continue to process more than 1 iteration, I suggest a small optimization:
Class[] argTypes = getArgTypes(objects);
-- to Line 219/220 (outside of the loop).Should be safe as the argument types don't change across the loop iterations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 fixed