I'm using SonarLint that shows me an issue in the following line.
LOGGER.debug("Comparing objects: " + object1 + " and " + object2);
Side-note: The method that contains this line might get called quite often.
The description for this issue is
"Preconditions" and logging arguments should not require evaluation
(squid:S2629)
Passing message arguments that require further evaluation into a Guava
com.google.common.base.Preconditions check can result in a performance
penalty. That's because whether or not they're needed, each argument
must be resolved before the method is actually called.
Similarly, passing concatenated strings into a logging method can also
incur a needless performance hit because the concatenation will be
performed every time the method is called, whether or not the log
level is low enough to show the message.
Instead, you should structure your code to pass static or pre-computed
values into Preconditions conditions check and logging calls.
Specifically, the built-in string formatting should be used instead of
string concatenation, and if the message is the result of a method
call, then Preconditions should be skipped altoghether, and the
relevant exception should be conditionally thrown instead.
Noncompliant Code Example
logger.log(Level.DEBUG, "Something went wrong: " + message); // Noncompliant; string concatenation performed even when log level too high to show DEBUG messages
LOG.error("Unable to open file " + csvPath, e); // Noncompliant
Preconditions.checkState(a > 0, "Arg must be positive, but got " + a); // Noncompliant. String concatenation performed even when a > 0
Preconditions.checkState(condition, formatMessage()); //Noncompliant. formatMessage() invoked regardless of condition
Preconditions.checkState(condition, "message: %s", formatMessage()); // Noncompliant
Compliant Solution
logger.log(Level.SEVERE, "Something went wrong: %s", message); // String formatting only applied if needed
logger.log(Level.SEVERE, () -> "Something went wrong: " + message); //since Java 8, we can use Supplier , which will be evaluated lazily
LOG.error("Unable to open file {}", csvPath, e);
if (LOG.isDebugEnabled() { LOG.debug("Unable to open file " + csvPath, e); // this is compliant, because it will not evaluate if log level is above debug. }
Preconditions.checkState(arg > 0, "Arg must be positive, but got %d", a); // String formatting only applied if needed
if (!condition) { throw new IllegalStateException(formatMessage()); // formatMessage() only invoked conditionally }
if (!condition) { throw new IllegalStateException("message: " + formatMessage()); }
I'm not 100% sure whether i understand this right. So why is this really an issue. Especially the part about the performance hit when using string concatenation. Because I often read that string concatenation is faster than formatting it.
EDIT: Maybe someone can explain me the difference between
LOGGER.debug("Comparing objects: " + object1 + " and " + object2);
AND
LOGGER.debug("Comparing objects: {} and {}",object1, object2);
is in the background. Because I think the String will get created before it is passed to the method. Right? So for me there is no difference. But obviously I'm wrong because SonarLint is complaining about it
I believe you have your answer there.
Concatenation is calculated beforehand the condition check. So if you call your logging framework 10K times conditionally and all of them evaluates to false, you will be concatenating 10K times with no reason.
Also check this topic. And check Icaro's answer's comments.
Take a look to StringBuilder too.
String concatenation means
LOGGER.info("The program started at " + new Date());
Built in formatting of logger means
LOGGER.info("The program started at {}", new Date());
very good article to understand the difference
http://dba-presents.com/index.php/jvm/java/120-use-the-built-in-formatting-to-construct-this-argument
Consider the below logging statement :
LOGGER.debug("Comparing objects: " + object1 + " and " + object2);
what is this 'debug' ?
This is the level of logging statement and not level of the LOGGER.
See, there are 2 levels :
a) one of the logging statement (which is debug here) :
"Comparing objects: " + object1 + " and " + object2
b) One is level of the LOGGER. So, what is the level of LOGGER object :
This also must be defined in the code or in some xml , else it takes level from it's ancestor .
Now why am I telling all this ?
Now the logging statement will be printed (or in more technical term send to its 'appender') if and only if :
Level of logging statement >= Level of LOGGER defined/obtained from somewhere in the code
Possible values of a Level can be
DEBUG < INFO < WARN < ERROR
(There can be few more depending on logging framework)
Now lets come back to question :
"Comparing objects: " + object1 + " and " + object2
will always lead to creation of string even if we find that 'level rule' explained above fails.
However,
LOGGER.debug("Comparing objects: {} and {}",object1, object2);
will only result in string formation if 'level rule explained above' satisfies.
So which is more smarter ?
Consult this url.
First let's understand the problem, then talk about solutions.
We can make it simple, assume the following example
LOGGER.debug("User name is " + userName + " and his email is " + email );
The above logging message string consists of 4 parts
And will require 3 String concatenations to be constructed.
Now, let's go to what is the issue of this logging statement.
Assume our logging level is OFF, which means that we don't interested in logging now.
We can imagine that the String concatenations (slow operation) will be ALWAYS applied and will not consider the logging level.
Wow, after understanding the performance issue, let's talk about the best practice.
Solution 1 (NOT optimal)
Instead of using String concatenations, we can use String Builder
StringBuilder loggingMsgStringBuilder = new StringBuilder();
loggingMsgStringBuilder.append("User name is ");
loggingMsgStringBuilder.append(userName);
loggingMsgStringBuilder.append(" and his email is ");
loggingMsgStringBuilder.append(email );
LOGGER.debug(loggingMsgStringBuilder.toString());
Solution 2 (optimal)
We don't need to construct the logging message before check the debugging level.
So we can pass logging message format and all parts as parameters to the LOGGING engine, then delegate String concatenations operations to it, and according to the logging level, the engine will decide to concatenate or not.
So, It's recommended to use parameterized logging as the following example
LOGGER.debug("User name is {} and his email is {}", userName, email);
Related
While working on Sonar static code analyzer I found some confusing (may be only to me) statement by Sonar on using parentheses.
Below are the few code snippets where Sonar says remove useless parentheses:
line>1 String auth = "Basic "+ com.somepackge.someMethod(((String) (parent.proxyUsername+ ":" + parent.proxyPassword)));
line>2 return rawtime.length() > 3 ? (rawtime.substring(0, rawtime.length() - 2) + rawtime.substring(rawtime.length() - 2, rawtime.length()).toLowerCase()) : rawtime;
though I have replaced above lines with below one to keep Sonar calm :) :
Line>3 String auth = "Basic "+ com.somepackge.someMethod((String) (parent.proxyUsername+ ":" + parent.proxyPassword));
Line>4 return rawtime.length() > 3 ? rawtime.substring(0, rawtime.length() - 2) + rawtime.substring(rawtime.length() - 2, rawtime.length()).toLowerCase() : rawtime;
So the reason for discussing this question is:
Actually using braces/parentheses are way to reduce the confusion so why to remove those parentheses.
What is best way to use parentheses while writing any complex statement in java.
See the line>1 and Line>4 here I think
(String) (parent.proxyUsername+ ":" + parent.proxyPassword)
this part of code should have the braces to avoid confusions but what Sonar expect is something like:
(String) parent.proxyUsername+ ":" + parent.proxyPassword
Any suggestion would be a great help. I got some links regarding this question but those were not much helpful.
First snippet
String auth = "Basic "+ someMethod(((String) (parent.proxyUsername+ ":" + parent.proxyPassword)));
You could rewrite it as:
String auth = "Basic "+ someMethod(parent.proxyUsername+ ":" + parent.proxyPassword);
because the string concatenation operator already does a string conversion. Unless you want a ClassCastException thrown when proxyUsername or proxyPassword are not Strings?
Second snippet
return rawtime.length() > 3 ? (rawtime.substring(0, rawtime.length() - 2) + rawtime.substring(rawtime.length() - 2, rawtime.length()).toLowerCase()) : rawtime;
The parenthesis is indeed unnecessary but the statement is quite unreadable. If you want to keep using the ternary operator I would suggest splitting the statement across lines:
return rawtime.length() > 3
? rawtime.substring(0, rawtime.length() - 2) + rawtime.substring(rawtime.length() - 2, rawtime.length()).toLowerCase()
: rawtime;
or you could revert the condition:
return rawtime.length() <= 3 ? rawtime :
rawtime.substring(0, rawtime.length() - 2) + rawtime.substring(rawtime.length() - 2, rawtime.length()).toLowerCase();
Line 1 has redundant parentheses, but Line 2's parentheses add clarity to the ternary statement.
Whether or not the extra parenthesis in 2 are useful is up for debate - but there's no reason not to remove the redundant ones in 1.
Generally it's best to use extra parenthesis to convey your intent about what the code should do, or to remove ambiguity in the order that things occur.
There is a semantic difference between these two versions:
(String) (parent.proxyUsername+ ":" + parent.proxyPassword)
(String) parent.proxyUsername+ ":" + parent.proxyPassword
In the first, the second set of () already evaluates to a String, implicitly calling parent.proxyUsername.toString() to convert proxyUsername to a String. So the cast is redundant and should be removed IMHO. The second version casts parent.proxyUsername to String, and will throw an exception is it hasn’t got runtime type String (only if it is declared a String is the cast redundant).
I agree that line 2 and 4 are complicated to read no matter if they have the redundant braces or not. Rewrite if you want clarity. That said, redundant braces are sometimes good for clarity IMHO, I do use them occasionally.
the best way is to put your class that you're casting to in a parentheses then the whole part to be converted in another parentheses, then include this whole code in a container parentheses, your code should look like this e.g ((String)(x+y)).
I hope that was helpful, thanks.
I'm using a combination of Java, the Play Framework using Java and not Scala, MongoDB and Jongo as my go between for a basic web CRUD app. I keep receiving a JSON parse exception even though my string doesn't contain any illegal characters. It's actually failing on closing curly bracket at the end of the statement. Below is my error and code. The query string is just a string builder, searching if an object is empty or has a value, if it has a value it's appended to a string.
Jongo method:
public static Iterable<OneDomain> findWithQueryString(String queryString){
return domains().find("{#}", queryString).as(OneDomain.class);
}
Controller Methods:
String builder example:
if(queryStringBuilder.toString().equalsIgnoreCase("")){
queryStringBuilder.append("date: {$gte : " + searchObj.dateFrom + ", $lt: " + searchObj.dateTo + "}");
}else{
queryStringBuilder.append(" , ");
queryStringBuilder.append("date: {$gte : " + searchObj.dateFrom + ", $lt: " + searchObj.dateTo + "}");
}
String queryString = queryStringBuilder.toString();
Iterable<OneDomain> filteredIterable = OneDomain.findWithQueryString(queryString);
Gives me this error:
Caused by: com.mongodb.util.JSONParseException:
{"source : Online Lists , blacklist : true , vetted : true , is : false , isNot : true"}
^
with the error on the "}" ending it.
In addition to that, if I try to escape the characters by putting in a \" so it becomes \"date\" it will parse and error out like so:
Caused by: com.mongodb.util.JSONParseException:
{"\"source\" : \"Online Lists\" , \"blacklist\" : true , \"vetted\" : true , \"is\" : false , \"isNot\" : true"}
Can I actually do this or because it's Java being inserted into it, the quotes will be around the whole string and thus it's trying to read it as a single JSON field vs it being the whole query?
First, make sure not to make your self vulnerable to injection attacks. Read up on injection attacks in general, and more specifically on MongoDB, eg OWASP page on Testing for NoSQL injection.
While you can indeed pass a generated query string into the find method I would not advise it. I did the same and had big problem when we generated a query containing the jongo substitution parameter #, ie
// This will throw an exception:
// java.lang.IllegalArgumentException: Not enough parameters passed to query: {"value":"#"}
...find("{" + "\"value\":\"#\"" + "}")
My solution is to pass a DBObject:
import com.mongodb.BasicDBObject
...find("#", new BasicDBObject().append("value", "#"))
It can also be built with the QueryBuilder:
import com.mongodb.QueryBuilder
...find("#", QueryBuilder.start("value").is("#").get())
It would be nice though to have query builder support directly in the Jongo API: https://github.com/bguerout/jongo/issues/173
Found the answer. Need to drop the substitution and instead my method looks like
domains().find("{"+queryString+"}").as(OneDomain.class);
I have a JSON String like the following:
json = "{\"Things\": \n" +
" {\"Thing\": {\n" +
" \"ID\":\"123\",\n" +
" \"name\":\"Yet Another Thing\",\n" +
" \"price\":\"$12.99\",\n" }\n" +
" }\n" +
"}";
Is there a way I can assert that the ID of Thing is 123 AND that it's name is "Yet Another Thing" in the same statement/assert?
At the moment, I seem to fail using filters:
JsonPath.read(json, "$.Things.Thing[?(#.ID == '123')].name")
I get the following exception:
java.lang.IllegalArgumentException: Invalid container object
Is that maybe because thereis no array notation [] in the JSON string above? Should there be?
On a related note, is there a good introduction to using Hamcrest (with JSON assert)? I know the official tutorial, but I always seem to get it wrong...
UPDATE: The rational for this was: what if I get several Thing elements back, about whose order I have no guarantee (so I can't match Thing[1] (unless I looped through them all))? How do I make sure one element has both, the right ID and the right name? If I check for the children separately, don't I run the risk that one Thing has the right name and another the right ID, but none has both? (Would that be possible with that JSON format, or would I have to an array in that case anyway, like "Thing": [ { ... }, { ... } ], ... ?
P.S.: I tried to use the JsonPath above as follows in the end: assertEquals("Yet Another Thing", JsonPath.read(json, "$.Things.Thing[?(#.ID == '123')].name"));
So that's where the exception might have come from, too. Also, I initially asked this question on the JsonPath mailing list, but didn't get any replies so far, so was hoping I might get help here quicker... :)
The tutorial gives the solution to your problem, it seems:
JsonAssert.with(json).assertThat("$.Things.Thing.ID", Matchers.equalTo("123"))
.assertThat("$.Things.Thing.name", Matchers.equalTo("Yet Another Thing"));
Our project contains many statements in the method chaining fluent style:
int totalCount = ((Number) em
.createQuery("select count(up) from UserPermission up where " +
"up.database.id = :dbId and " +
"up.user.id <> :currentUserId ")
.setParameter("dbId", cmd.getDatabaseId())
.setParameter("currentUserId", currentUser.getId())
.getSingleResult())
.intValue();
I've got checkstyle mostly configured to match our existing code style, but now it's failing on these snippets, preferring instead:
int totalCount = ((Number) em
.createQuery("select count(up) from UserPermission up where " +
"up.database.id = :dbId and " +
"up.user.id <> :currentUserId ")
.setParameter("dbId", cmd.getDatabaseId())
.setParameter("currentUserId", currentUser.getId())
.getSingleResult())
.intValue();
Which is totally inappropriate. Is there anyway to configure checkstyle to accept the method chaining style? Is there an alternate tool I can run from maven to enforce this kind of indentation?
I never made this work in Eclipse so we barely use Format Source. In the end it is often best to extend. We tried hard and failed. It was one and half year ago. In the end we use formatting text only in Eclipse by Selecting the line or to preformat before we format by hand.
Usually the formating done by a engineer carries a certain meaning. And so automatic format will never work. Especially if you do something like
public static void myMethod(
int value, String value2, String value3)
If you autoformat this it fails similar to your example.
So feel free to join the club of not using automatic formatting beside as a step before you format it the human way.
with intellij , it can be done by selecting "align when multiline" in case of "method chain calls" so i guess this property is misconfigured in the configurations.
The situation is easy. I created a rules file:
package org.domain.rules;
dialect "mvel"
import eu.ohim.fsp.core.configuration.domain.xsd.Section;
global java.lang.String sectionName;
rule "rule 1"
salience 1000
when
Section($name : nameOfTheSection)
eval(sectionName == null)
then
System.out.println("Section: " + $name+ "("+$name.length()+")");
System.out.println("Section Name: " + sectionName + "("+sectionName.length()+")");
System.out.println("Mark Details: " + sectionName.equals(null));
end
And before firing the rules I added the Section object with a valid coreName and the globals:
public void fireInserted(Section section1) {
kstateful.insert(section1);
kstateful.setGlobal("sectionName", new String("markudetails"));
kstateful.fireAllRules();
}
The result is:
Section: markudetails(12)
Section Name: markudetails(12)
Mark Details: false
QUESTION: How can it be possible? in when part is null and in then part is not null!!!
Global vars are not a part of the knowledge base, but a separate channel to push some context into the rule execution. It is not appropriate to use them in a when clause. The exact reason why it was null in your case may be hard to trace, since rule activation is completely decoupled from rule execution. The variable may simply not be bound at when clause evaluation time, but is bound at then clause execution time.
To summarize: don't use globals in a when clause, that's not what they are for.
Your problem has an easy general solution: you can insert a configuration object into the knowledge. That object can have your desired "sectionName" property which you will then find easy to test in a when.
As an aside, it is meaningless to test for object.equals(null) -- this can never produce true. There is also no need to use new String("markudetails"). Instead use just "markudetails".