java.util.Collections $ UnmodifiableMap问题:包含代码

时间:2009-11-26 14:01:50

标签: java google-app-engine facebook gwt map

我正在使用GWT构建一个facebook平台网络应用程序并将其托管在App Engine上。

我正在添加使用回调网址中提供的查询字符串参数的验证代码。 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();

}

4 个答案:

答案 0 :(得分:2)

在HashMap中复制Windows.Location.getParameterMap(),它将起作用:

所以你通过RPC发送新的HashMap&gt;(Windows.Location.getParameterMap())。

问题是对于GWT,unmodifiableMap不是Serializable。我知道它有一个Serializable标记,但在GWT中它的工作方式有点不同。大多数集合类都有自定义GWT实现,有些不是100%兼容。

答案 1 :(得分:1)

我没有看到任何不可修改的集合。

您的代码非常复杂。如果我理解正确,那么这应该是等价的。我不会使用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();
}

在重构时我发现了一个可能的错误:当你在代码中创建有序地图时,你会认为地图中的所有列表都不是空的。所以第一个空列表将在第一个循环中引起NPE。

答案 2 :(得分:0)

执行System.out.println(fbQueryStringParams.getClass());在消息的开头(或记录它或你需要能够看到的是什么)。

如果该参数从系统传递给您,则它很可能被包装为不可修改的集合,因为它们不希望您更改它。

答案 3 :(得分:0)

我是否正确理解您在客户端代码中执行Window.Location.getParameterMap并在RPC调用中将其发送到服务器?在那种情况下......问题是:ParameterMap是否可序列化?并非所有实现都支持GWT。所以它可能只是你的服务器代码甚至没有被调用,但它在发送请求之前崩溃了。你在GWT编译期间看到了什么警告吗?

代码,虽然可以清理实现,实际上你可以有一个NPE,但不是修改提供的参数Map或Map值中的List。所以问题可能在其他地方。

为什么不在托管模式(或在GWT 2.0中调用它的开发模式)下运行应用程序?

大卫