I need to log all the request parameters in some situations for debug purposes...
I tried using ToStringBuilder.reflectionToString(request), but it still showed memory addresses
Is there any easy way to log request parameters in plain text so that I could do something
logger.info(ToStringBuilder.reflectionToString(request)); ?
I also tried logger.info(ToStringBuilder.reflectionToString(request.getParameterMap());
reflectionToString only uses reflection on the object given, to find the attributes to print. The attributes themselves are output using their toString() methods.
Neither the request nor the parameter map have the request parameters you are interested in as direct attributes, so reflectionToString fails for you.
I know of no OOTB way to deeply reflection-print an object, in JDK or commons-lang.
What does the simple call
logger.info(request.getParameterMap());
produce for you?
Ah, I see: The parameter values are String arrays, which only print their hashcode.
You might try a helper function like this (disclaimer: uncompiled and untested)
public static String getParameterToString(ServletRequest request){
StringBuilder sb = new StringBuilder("{");
for (Map.Entry<String, String[]> entry : request.getParameterMap().entrySet()){
sb.append(entry.getKey()).append(":");
sb.append(Arrays.toString(entry.getValue())).append(",");
}
if (sb.length() > 1)
sb.setLength(sb.length() - 1);
return sb.append("}").toString();
}
This function is tested
public static String dumpParams(ServletRequest req) {
StringBuilder sb = new StringBuilder();
Set<Map.Entry<String, String[]>> entries = req.getParameterMap().entrySet();
for (Map.Entry<String, String[]> entry : entries) {
sb.append(entry.getKey())
.append(" = ")
.append(Arrays.toString(entry.getValue()))
.append(", ");
}
if (sb.length() > 2)
sb.setLength(sb.length() - 2); //Removes the last comma
return sb.toString();
}
Related
I would like to write a method that map the input string to its coresponding value, ex: when I input separator = "Semicolon", then I would like to return its value as ';'. What I am doing right now is using switch case:
switch (separator) {
case "Semicolon":
return ';';
case "Comma":
return ',';
default:
return '-';
}
I just would like to ask that beside using switch case here, are there any other solutions that having the same effect but might be shorter line of code. Thank you in advanced!
The simply answer lies in your wording: mapping. As in:
Map<String, Character> operatorsByName = new HashMap<>();
operatorsByName.put("Comma", ',');
and so on. Or with Java9, the nice new of() method to create maps from "literals".
And then you can just get() based on incoming strings. Alternatively, enum, and EnumSet could come in handy.
You can use java.util.Map to maintain the mapping dynamically.
Map<String, Character> map = new HashMap<>();
map.put("Semicolon", ';');
map.put("Comma", ',');
return map.getOrDefault(separator, '-');
or you can use Map.of() factory method:
Map<String, Character> map = Map.of("Semicolon", ';', "Comma", ',');
return map.getOrDefault(separator, '-');
A more elegant way would be to use an enum instead:
public enum Separator {
COMMA(","),
SEMICOLON(";");
private String symbol;
Separator(String code) {
this.symbol = code;
}
public String symbol() {
return this.symbol;
}
}
and then you can use it like:
String comma = Separator.COMMA.symbol();
and as suggested one can also use:
Separator.valueOf("COMMA").symbol()
Maybe in this way, you don't need any switch / case at all.
I can imagine that you could pass the "right" Separator that is needed.
Let's say i have a string like this: "/c1/client/{clientId}/field/{fieldId}/version/{versionId}/file/{filename}"
I want to replace all values inside curly brackets with the actual values, so the link would look like this:
"c1/client/Tudor/field/search/version/1/file/hello.txt".
How can i do that in a way that does not limit the number of parameters used? Because i have some requests with 4 parameters (like this one) and others with only one parameter, or none. What is the best way to do this?
Edit: I would need something like: Search string for any values between {}. If string contains {value}, take all {values} and replace with parameter.
You can parse #pathParameters and redirect to the address you create with spring #controller. If these are request as you wrote that is the right approach.
In case of String:
var u = "/c1/client/{clientId}/field/{fieldId}/version/{versionId}/file/{filename}";
u.replace(/\{clientId\}/, "Tudor").replace(/\{fieldId\}/, "search").replace(/\{versionId\}/, 1).replace(/\{filename}/, "hello.txt");
You can try this
String str = "/c1/client/{clientId}/field/{fieldId}/version/{versionId}/file/{filename}";
Map<String, String> map = new HashMap<String, String>();
map.put("clientId", "Tudor");
map.put("fieldId", "search");
map.put("versionId", "1");
map.put("filename", "hello.txt");
for (Map.Entry<String, String> entry : map.entrySet()) {
str = str.replace("{" + entry.getKey() + "}", entry.getValue());
}
String newStr = str.substring(1);
System.out.println(newStr);
I've run into a common situation where i have a list of objects and need to generate a comma separated string with a single property, which are then each surrounded by single quotes.
2 Examples
public String partIDsToString(List<Part> parts){
StringBuilder sb = new StringBuilder();
for(Part part : parts)
sb.append("'"+part.getPartNumber() + "',");
return sb.substring(0,sb.length()-1);
}
public String companyIDsToString(List<Company> parts){
StringBuilder sb = new StringBuilder();
for(Company c : parts)
sb.append("'"+c.getId() + "',");
return sb.substring(0,sb.length()-1);
}
I'll need to create more methods like this in the future, and was wondering if there was a way to generalize this functionality, im looking for something like this.
public String objectPropertyToString(List<Object> list, Method getProperty){
StringBuilder sb = new StringBuilder();
for(Object obj: list)
sb.append("'"+obj.getProperty() + "',");
return sb.substring(0,sb.length()-1);
}
List<Company> companies = getCompaniesList();//not important
String result = objectPropertyToString(companies , Company::getId);
List<Part> parts= getPartsList();//not important
String result = objectPropertyToString(parts, Part::getPartNumber);
Can this be done using method references/lambdas, or any other way?
Stream.map() and Collectors.joining() are your friends here.
companies.stream()
.map(Company::getId)
.map(s -> "'" + s + "'")
.collect(joining(","));
You can create a helper method, but in my judgement the above is succinct enough that it isn't worthwhile:
static <T> String mapAndJoin(Collection<T> c, Function<T,String> f){
return c.stream()
.map(f)
.map(s -> "'" + s + "'")
.collect(joining(","));
}
mapAndJoin(companies, Company::getId);
As shown in this answer, Collectors.joining can be used to produce a comma separator list. But you can also use it to enclose the elements in single quotes in one go, which is more efficient than doing this in a separate String operation per element before joining them.
The basic idea is as follows:
public static <T> String objectPropertyToString(
Collection<? extends T> list, Function<T,String> f) {
return list.stream().map(f).collect(Collectors.joining("', '", "'", "'"));
}
Instead of just separating the elements with just a comma, we separate with a closing single quote, followed by comma and an opening single quote. Further, we use an opening single quote as starter before the first element and a closing single quote after the last element.
This works smoothly, with only one exception: if the list is empty, we get a sole '' as result because the initial opening quote and trailing closing quote is always produced. To solve this, we have to retrace what Collectors.joining does internally, to get hands on the StringJoiner used for the process, so we can configure it in a way not offered by the built-in collector:
public static <T> String objectPropertyToString(
Collection<? extends T> list, Function<T,? extends CharSequence> f) {
return list.stream().map(f).collect(
()->new StringJoiner("', '", "'", "'").setEmptyValue(""),
StringJoiner::add, StringJoiner::merge).toString();
}
This basically does the same as the previous attempt with the notable exception that we now can use setEmptyValue to specify the different result for the case that there were no elements. As a bonus we can now relax the generic type signature, allowing arbitrary CharSequence instances to be joined instead of just Strings.
The usage is as before:
List<Company> companies = getCompaniesList();//not important
String result = objectPropertyToString(companies , Company::getId);
List<Part> parts= getPartsList();//not important
String result = objectPropertyToString(parts, Part::getPartNumber);
I want to generate a Get query string in java like so
www.example.com/somethingToGet?key1=value&key2=value....
So my method has 2 parameters the base url(www.example.com/somethingToGet) is the first argument and the 2nd argument is a map data structure. I want to iterate over the map and generate a string like so
key1=value&key2=value....
It shouldn't end with ampersand.
I don't want to use any built in functions, I want to know the logic how such strings are generated.
Something like this:
public static String getQuery(String base, java.util.Map<String, String> map) {
StringBuilder str = new StringBuilder(base);
str.append('?');
boolean first = true;
for (java.util.Map.Entry<String, String> e : map.entrySet()) {
if (first)
first = false;
else
str.append('&');
str.append(e.getKey());
str.append('=');
str.append(e.getValue());
}
return str.toString();
}
You can also use the format method in URLEncoder class from the Apache HttpComponents library to create a query string. As per the documentation it
Returns a String that is suitable for use as an application/x-www-form-urlencoded list of parameters in an HTTP PUT or HTTP POST.
I am building a facebook platform web app using GWT and hosting it on App Engine.
I am adding validation code that uses supplied query string parameters in the callback url. GWT allows me to get these parameters by calling Window.Location.getParameterMap() and the returned Map is immutable.
I may be wrong however I think this problem has nothing to do with FB, GWT or App Engine specifically and is more down to my misunderstanding something about Map objects.
I don't think that my code attempts to modify the supplied Map but the error I get seems to suggest that my code is trying to modify an immutable Map.
Can someone please take a look and let me know where I am modifying an unmodifiable Map?
I would supply a stack trace but I can't find a way to get a stack trace for this to display in App Engine logs.
Thanks in advance for any and all help :-)
/**
* Validation Test
* To generate the signature for these arguments:
* 1. Remove the fb_sig key and value pair.
* 2. Remove the "fb_sig_" prefix from all of the keys.
* 3. Sort the array alphabetically by key.
* 4. Concatenate all key/value pairs together in the format "k=v".
* 5. Append your secret key.
* 6. Take the md5 hash of the whole string.
* #param fbQueryStringParams
* #return String
*/
public String test(Map<String,List<java.lang.String>> fbQueryStringParams) {
String appSecret = TinyFBClient.APP_SECRET;
String fbSig = fbQueryStringParams.get("fb_sig").get(0);
StringBuilder sb = new StringBuilder();
TreeMap<String,String> sortedMap = new TreeMap<String,String>();
// Get a Set view of the Map of query string parameters.
Set<Map.Entry<String,List<java.lang.String>>> mapEntries = fbQueryStringParams.entrySet();
// Iterate through the Set view, inserting into a SortedMap all Map.Entry's
// that do not have a Key value of "fb_sig".
Iterator<Map.Entry<String,List<java.lang.String>>> i = mapEntries.iterator();
while(i.hasNext()) {
Map.Entry<String,List<java.lang.String>> mapEntry = i.next();
if(!mapEntry.getKey().equals("fb_sig")) { // 1. Remove the fb_sig key and value pair.
sortedMap.put(mapEntry.getKey(),mapEntry.getValue().get(0)); // 3. Sort the array alphabetically by key.
}
}
// Get a Set view of the Map of alphabetically sorted Map.Entry objects.
Set<Map.Entry<String,String>> sortedMapEntries = sortedMap.entrySet();
// Iterate through the Set view, appending the concatenated key's and value's
// to a StringBuilder object.
Iterator<Map.Entry<String,String>> ii = sortedMapEntries.iterator();
while(ii.hasNext()) {
Map.Entry<String,String> mapEntry = ii.next();
// 4. Concatenate all key/value pairs together in the format "k=v".
sb.append(mapEntry.getKey().replaceAll("fb_sig_","")); // 2. Remove the "fb_sig_" prefix from all of the keys.
sb.append("=");
sb.append(mapEntry.getValue());
}
sb.append(appSecret); // 5. Append your secret key.
String md5 = DigestUtils.md5Hex(sb.toString()); // 6. Take the md5 hash of the whole string.
// Build and return an output String for display.
StringBuilder output = new StringBuilder();
output.append("fbSig = "+fbSig);
output.append("<br/>");
output.append("md5 = "+md5);
return output.toString();
}
copy the Windows.Location.getParameterMap() in a HashMap and it will work:
So you send new HashMap>( Windows.Location.getParameterMap()) over RPC that works.
The problem is that unmodifiableMap is not Serializable for GWT. I know that it has a Serializable marker, but in GWT it works a little bit different. Most collection classes have a custom GWT implementation and some are not 100% compatible.
I don't see any unmodifiable collections.
Your code is pretty complicated. If I understood it right, then this should be equivalent. I wouldn't use Map.Entry objects and the TreeMap has a handy constructor for your needs. And finally, I'd prefer the 'forall' loop over the iterator.
public String test(Map<String, List<java.lang.String>> fbQueryStringParams) {
String appSecret = TinyFBClient.APP_SECRET;
String fbSig = fbQueryStringParams.get("fb_sig").get(0);
StringBuilder sb = new StringBuilder();
TreeMap<String, List<String>> sortedMap = new TreeMap<String, List<String>>(fbQueryStringParams);
sortedMap.remove("fbSig"); // remove the unwanted entry
for (String key, sortedMap.keySet()) {
List<String> values = sortedMap.get(key);
String printableKey = key.replaceAll("fb_sig_", ""));
String value = "EMPTY LIST";
if (!values.isEmpty()) {
// This could have been your problem, you always
// assume, all lists in the map are not empty
value = values.get(0);
}
sb.append(String.format("%s=%s", printableKey, value);
}
sb.append(appSecret);
String md5 = DigestUtils.md5Hex(sb.toString());
// Build and return an output String for display.
StringBuilder output = new StringBuilder();
output.append("fbSig = " + fbSig);
output.append("<br/>");
output.append("md5 = " + md5);
return output.toString();
}
While refactoring I found one possible bug: when you create the sorted map in your code, you assume, all lists in the map are not empty. So the first empty list will cause a NPE in the first loop.
Do a System.out.println(fbQueryStringParams.getClass()); at the start of the message (or log it or whatever you need to be able to see what it is).
If that argument is passed to you from the system it is very likely wrapped as an unmodifiable collection since they don't want you altering it.
Did I understand it correctly that you are doing a Window.Location.getParameterMap in your client code and sending it to the server in a RPC call ? In that case ... the question is: is that ParameterMap serializable ? Not all implementations are in fact supported in GWT. So it might just be that your server code is not even called but that it crashes before it can send the request. Did you see any warning during GWT compilation ?
The code, although the implementation can be cleaned up and indeed you can have a NPE, is NOT modifying the supplied parameter Map or the List in the Map values. So the problem is probably somewhere else.
Why don't you run your application in hosted mode (or development mode as they call it in GWT 2.0) ?
David