Java: how to format string with logging-like pattern? - java

Sometimes it's required to compose a string using logging-like pattern:
Something went wrong, detailA={}, detailB={}
but not in order to log it, but for other purposes (e.g. send over network).
Sometimes it's causing code duplication, like this:
logger.info("Something went wrong, detailA={}, detailB={}", detailA, detailB));
otherSystem.info(String.format("Something went wrong, detailA=%s, detailB=%s",
detailA, detailB);
Which is highly inconvenient and error-prone. How to format a string using logging pattern and logging-like API?

If you using slf4j, you can use its MessageFormatter.arrayFormat which returns a FormattingTuple,
FormattingTuple ft = MessageFormatter.arrayFormat(format, argArray);
And then convert it to a string.
String msg = ft.getMessage();
Which means you can add a utility method for that
static String format(String format,String...params){
FormattingTuple ft = MessageFormatter.arrayFormat(format,params);
String message = ft.getMessage();
return message;
}
System.out.println(format("hi {}, you {}","there","rock"));

Related

How to avoid escaping /n on each microservice in the chain? Java

For example, we are getting a json message like this from our partner:
{
"message": "Dear client,\nWe'd like to offer you.."
}
The partner wants the client to receive the message like this (without newline but with \n)
Dear client,\nWe'd like to offer you
But we have a chain of microservices in our ecosystem and that json goes through 4 or 5 microservices which proccesing it before client can get it. So, our partner should give us \\\\\\\\n instead of \n in order to client got \n in the result. But I'm wondering, is adding 8 backslashes in the source message to escape "\n" for every microservice the only way to solve this problem. I think it's not really good solution, because we have to make changes in source message if count of microservices in chain changes (Moreover, we will face he problem if count of microservices in the chain start changing dynamically)? Is there way to use \n in source message (from partner) without replacing every \n with \\n in our microservies?
There is an example how I process the json in one of the microservices:
private String replace(String sourceJson, List<String> properties, DocumentContext context) {
StringBuilder stringBuilder = new StringBuilder(sourceJson);
for (String property : properties) {
String newValue = Pattern.compile("ABC")
.matcher(stringBuilder)
.replaceAll(context.read(property, String.class));
stringBuilder.replace(0, stringBuilder.length(), newValue);
}
return stringBuilder.toString();
}
Here's an example of creating a JSON object, turning it into a String and and then back into a JsonObject, adding a property, and turning it back into a String again.
package org.example;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
public class App {
public static void main(String[] args) {
JsonObject o = new JsonObject();
o.addProperty("message","foo\nbar");
Gson gson = new Gson();
String stringRep = gson.toJson(o);
System.out.println(stringRep);
JsonObject o2 = gson.fromJson(stringRep, JsonObject.class);
o2.addProperty("newProp", 42);
String messageValue = o2.get("message").getAsString();
System.out.println(messageValue);
String newMessageValue = messageValue.replace("foo", "baz");
o2.addProperty("message", newMessageValue);
stringRep = gson.toJson(o2);
System.out.println(stringRep);
}
}
The output of this program is:
{"message":"foo\nbar"}
foo
bar
{"message":"baz\nbar","newProp":42}
So you can see the the Java representations of strings contain newline characters, but the JSON representations contain the character sequence '\', 'n'.
The maven dependency you need is:
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.9</version>
</dependency>

How to get the browser name alone from client in java?

I tried using
String userAgent=req.getHeader("user-agent");
and also the following
#GET
#Path("/get")
public Response addUser(#HeaderParam("user-agent") String userAgent) {
return Response.status(200)
.entity("addUser is called, userAgent : " + userAgent)
.build();
}
But I need only, browser name as chrome,firefox,IE.Please help,if anyone know.
UPDATE : Got answer
public String browser(#HeaderParam("user-agent") String userAgent){
UserAgent browserName = UserAgent.parseUserAgentString(userAgent);
String browser=browserName.toString();
System.out.println(browser)
}
Getting information out of user agent strings is somewhat of a black art. Easiest is probably to use a library to parse the user agent string and extract the needed information.
I've used UADetector in the past with good results, but there are undoubtedly other libraries out there.
The following sample is from the UADetector documentation:
UserAgentStringParser parser = UADetectorServiceFactory.getResourceModuleParser();
ReadableUserAgent agent = parser.parse(request.getHeader("User-Agent"));
out.append("You're a <em>");
out.append(agent.getName());
out.append("</em> on <em>");
out.append(agent.getOperatingSystem().getName());
out.append("</em>!");

How can I get Gson's single escaping to play along with Javascript's double escaping of special characters and symbols?

As a follow up on my previous question: Why doesn't my attempt to escape quotation marks in JSON work?, I would like to know if there is any way to make Gson and Javascript play along when escaping special characters and symbols.
Consider this as a database table string, that I want to display on a web page:
I am "literally" hurting inside because this do not work!
If I retrieve the string in Java from my database, and use Gson to parse it, it will look like this:
'{"text" : "I am \"literally\" hurting inside that this does not work!"}'
However, my Javascript function for parsing needs this to display it correctly:
'{"text" : "I am \\"literally\\" hurting inside that this does not work!"}'
Are there any way to fix this, other then to check for substrings in Java, and adding an extra \?
I would suggest you to use Unbescape [ http://www.unbescape.org ]
It allows you to escape JavaScript string literals (among other things like HTML, XML, JSON...), so you can pass you GSON string to it simply like:
final String escaped = JavaScriptEscape.escapeJavaScript(text);
And it will give you the JavaScript-escaped string you need.
Disclaimer, per StackOverflow rules: I'm Unbescape's author.
Try this one, it will work in all the cases:
{\"text\" : \"I am \\\"litteraly\\\" hurting inside that this does not work!\"}
Sample code:
Using JSONObject:
String str = "{\"text\" : \"I am \\\"litteraly\\\" hurting inside that this does not work!\"}";
try {
System.out.println(new JSONObject(str).getString("text"));
} catch (JSONException e) {
e.printStackTrace();
}
Using Gson:
class Text implements Serializable{
private String text;
...
}
Gson gson = new Gson();
String str = "{\"text\" : \"I am \\\"litteraly\\\" hurting inside that this does not work!\"}";
System.out.println(gson.fromJson(str, Text.class).text);
Firefox Firebug plugin snapshot:
String str = "{\"text\" : \"I am \\\"litteraly\\\" hurting inside that this does not work!\"}";
try {
System.out.println(new JSONObject(str).getString("text"));
} catch (JSONException e) {
e.printStackTrace();
}

How to convert a String FIX message to FIX FIX50SP2 format using QuickFixJ

Need a quick help. I am a newbie in QuickFixJ. I have a FIX message in a txt file. I need to convert that into FIX50SP2 format. I am enclosing the code snippet.
String fixMsg = "1128=99=25535=X49=CME34=47134052=20100318-03:21:11.36475=20120904268=2279=122=848=336683=607400107=ESU2269=1270=140575271=152273=121014000336=2346=521023=1279=122=848=336683=607401107=ESU2269=1270=140600271=206273=121014000336=2346=681023=210=159";
System.out.println("FixMsg String:"+fixMsg);
Message FIXMessage = new Message();
DataDictionary dd = new DataDictionary("FIX50SP2.xml");
FIXMessage.fromString(fixMsg, dd, false);
System.out.println("FIXMessage Output:" + FIXMessage.toString()); // Print message after parsing
MsgType msgType = new MsgType();
System.out.println(FIXMessage.getField(msgType));
Here is the output:
FixMsg String:1128=99=15835=X49=CME34=47164052=2012090312102051175=20120904268=1279=122=848=336683=607745107=ESU2269=1270=140575271=123273=121020000336=2346=501023=110=205
FIXMessage Output:9=6135=X34=47164049=CME52=2012090312102051175=20120904268=110=117
quickfix.FieldNotFound: Field [35] was not found in message.
at quickfix.FieldMap.getField(FieldMap.java:216)
at quickfix.FieldMap.getFieldInternal(FieldMap.java:353)
at quickfix.FieldMap.getField(FieldMap.java:349)
at MainApp.main(MainApp.java:52)
I want to extract MsgType field (field 35). Could you please tell me where I am wrong? The thing I have observed is that after parsing to FIX50SP2 format, the convert FIX message is missing many data element (for details see the output)
Thanks
Like others mentioned the MsgType is an header field and you get it by using the following
String msgType = null;
if(FIXMessage.getHeader().isSetField(MsgType.FIELD)) {
msgType = FIXMessage.getHeader().getString(MsgType.FIELD);
}
System.out.println("MsgType is " + msgType);`
The reason you are missing many data element after parsing is, probably your message have some custom tags(like tag 2346), which is not defined in your data dictionary(FIXSP02.xml). hence the parsing of those tags failed and missing in the output.
To fix this, get the data dictionary from the party that is sending you the message and use it to parse the message
I'm not familiar with FIX messages and QuickFixJ, but glancing at the Javadoc, it seems like you should use the identifyType method :
String fixMsg = "1128=99=25535=X49=CME34=47134052=20100318-03:21:11.36475=20120904268=2279=122=848=336683=607400107=ESU2269=1270=140575271=152273=121014000336=2346=521023=1279=122=848=336683=607401107=ESU2269=1270=140600271=206273=121014000336=2346=681023=210=159";
MsgType msgType = Message.identifyType(fixMsg);
You may find FixB framework useful as it deals well with non-standard use cases of FIX.
As in your case, to extract only data you are interested in, you need to define a class that will represent this data and to bind it to FIX using annotations. E.g.:
#FixBlock
public class MDEntry {
#FixField(tag=269) public int entryType; // you could define an enum type for it as well
#FixField(tag=278) public String entryId;
#FixField(tag=55) public String symbol;
}
...
FixFieldExtractor fixExtractor = new NativeFixFieldExtractor();
List<MDEntry> mdEntries = fixExtractor.getGroups(fixMsg, List.class, 268, FixMetaScanner.scanClass(MDEntry.class))
In more common cases, FixSerializer interface should be used, but it requires a message with MsgType(35) tag and a class annotated with #FixMessage(type="...") accordingly. E.g.:
#FixMessage(type="X")
public class MarketData {
#FixGroup(tag=268) public List<MDEntry> entries;
}
...
FixMetaDictionary fixMetaDictionary = FixMetaScanner.scanClassesIn("my.fix.classes.package");
FixSerializer fixSerializer = new NativeFixSerializer("FIX.5.0.SP2", fixMetaDictionary);
MarketData marketData = fixSerializer.deserialize(fixMsg);
I hope you will find it useful.
If you need just a MsgTyp, you're sure the message is correct and you do not need any other field from the message, then I would recommend extracting MsgType from string using regexp.
e.g.: \u000135=(\w+)\u0001
It is MUCH FASTER than parsing (and validating) a string via QuickFix.

How can I used named parameters in a messages.properties file?

Is there any way to have message.properties records as follows
message.myMessage=This message is for ${name} in ${location}
as opposed to
message.myMessage = This message is for {0} in {1}
When I am creating the messages, I don't neccessarily know the order / how many parameters are needed, but I am able just pass in several properties by name, and just the correct ones would be used.
After facing the very same question and poking in source code I found a "loop-hole" that makes it possible in a very easy way:
message.myMessage = This message is for {0,,name} in {1,,location}
This approach doesn't eliminate usage of numbers. The reason to use it is to give hints to translation folks.
I am afraid not, parameters are an Object array so there is no way to define names for them. If you always passes in the array of parameter in the same order though you could use them like this:
message.myMessage = This message is for {0} in {1}
message.myNameMessage = This message is for {0}
message.myLocationMessage = This message is for people in {1}
message.myAlternateMessage = The message params are location: {1}; name: {0}
Take a look at ICU4J
It allows for something like this:
message.myMessage=This message is for {name} in {location}.
And it is way more powerful than the simple replacements suggested, because can do locale aware formatting of the parameters (ie: "Subscription expires on: {expirationDate, date, long})
http://icu-project.org/apiref/icu4j/com/ibm/icu/text/MessageFormat.html
Unfortunately the MessageFormat API does not support named parameters, only argument-index:
Patterns and Their Interpretation
MessageFormat uses patterns of the following form:
MessageFormatPattern:
String
MessageFormatPattern FormatElement String
FormatElement:
{ ArgumentIndex }
{ ArgumentIndex , FormatType }
{ ArgumentIndex , FormatType , FormatStyle }
Everything is possible for those who try... I never heard about something like that for Java, but you can write it by yourself.
Please take a look at this example:
public String format(String message, String... arguments) {
for (String argument : arguments) {
String[] keyValue = argument.split("=");
if (keyValue.length != 2)
throw new IllegalArgumentException("Incorrect argument: " + argument);
String placeholder = "${" + keyValue[0] + "}";
if (!message.contains(placeholder))
throw new IllegalArgumentException(keyValue[0] + " does not exists.");
while (message.contains(placeholder))
message = message.replace(placeholder, keyValue[1]);
}
return message;
}
It is not ideal, as you actually would call it with hardcoded string (which is generally bad idea) and you would be forced to use Strings only, but it can be done. The only question is if it is practical.
It is possible using apache commons lang library.
https://commons.apache.org/proper/commons-lang/
Properties messages = ...
Map<String, String> m = new HashMap<>();
m.put("name", "Mithu");
m.put("location", "Dhaka");
StrSubstitutor sub = new StrSubstitutor(m);
String msg = sub.replace(messages.getProperty("message.myMessage"));
// msg = This message is for Mithu in Dhaka

Categories

Resources