java.util.Collections $ UnmodifiableMapの問題:コードが含まれています
-
05-07-2019 - |
質問
GWTを使用してFacebookプラットフォームのWebアプリを構築し、App Engineでホストしています。
コールバックURLで指定されたクエリ文字列パラメーターを使用する検証コードを追加しています。 GWTでは、Window.Location.getParameterMap()を呼び出すことでこれらのパラメーターを取得でき、返されたMapは不変です。
間違っている可能性がありますが、この問題はFB、GWT、またはApp Engineとは特に関係がなく、Mapオブジェクトについての私の誤解によるものだと思います。
コードが提供されたMapを変更しようとは思わないが、表示されるエラーは、コードが不変のMapを変更しようとしていることを示唆しているようだ。
誰かが見て、変更できないマップを変更している場所を教えてもらえますか?
スタックトレースを提供しますが、これのスタックトレースを取得してApp Engineログに表示する方法が見つかりません。
すべてのヘルプに感謝します:-)
/**
* 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();
}
解決
HashMapでWindows.Location.getParameterMap()をコピーすると、動作します:
したがって、動作するRPCを介して新しいHashMap&gt;(Windows.Location.getParameterMap())を送信します。
問題は、unmodifiableMapがGWTに対してSerializableではないことです。 Serializableマーカーがあることは知っていますが、GWTでは少し動作が異なります。ほとんどのコレクションクラスにはカスタムGWT実装があり、一部は100%互換性がありません。
他のヒント
変更できないコレクションは表示されません。
コードはかなり複雑です。私がそれを正しく理解していれば、これは同等のはずです。 Map.Entryオブジェクトは使用せず、TreeMapには必要に応じて便利なコンストラクターがあります。そして最後に、イテレーターよりも「forall」ループを好むでしょう。
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();
}
リファクタリング中に、バグの可能性が1つ見つかりました。コードでソートされたマップを作成すると、マップ内のすべてのリストが空ではないことを前提としています。したがって、最初の空のリストは、最初のループでNPEを引き起こします。
System.out.println(fbQueryStringParams.getClass());を実行します。メッセージの冒頭(または、ログに記録するか、メッセージを確認するために必要なものをすべて記録します)。
システムから引数が渡された場合、変更を望まないため、変更不可能なコレクションとしてラップされる可能性が非常に高くなります。
クライアントコードでWindow.Location.getParameterMapを実行し、RPC呼び出しでサーバーに送信していることを正しく理解しましたか?その場合...質問は:ParameterMapシリアル化可能か?実際、GWTではすべての実装がサポートされているわけではありません。そのため、サーバーコードが呼び出されず、リクエストを送信する前にサーバーコードがクラッシュする場合があります。 GWTのコンパイル中に警告が表示されましたか?
コードは、実装をクリーンアップでき、実際にNPEを使用できますが、提供されたパラメーターMapまたはMap値のリストを変更していません。そのため、問題はおそらく他のどこかにあります。
アプリケーションをホストモード(またはGWT 2.0で呼び出される開発モード)で実行しないのはなぜですか?
デビッド