2009-11-26 31 views
0

我正在構建一個使用GWT並將其託管在App Engine上的Facebook平臺Web應用程序。

我正在添加使用提供的查詢字符串參數在回調url中的驗證代碼。 GWT允許我通過調用Window.Location.getParameterMap()來獲得這些參數,並且返回的Map是不可變的。

我可能是錯的,但我認爲這個問題與FB,GWT或App Engine沒有任何關係,更多的是我對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(); 

} 
+1

也請發佈錯誤消息,並指出它抱怨哪一行。 –

+0

看起來不錯,但哪張地圖是不可修改的?該方法的參數? –

+0

對不起,我是新來這個網站,並錯過了您的意見。 完整的錯誤消息和行將是完美的,但我努力從Throwable.getMessage()產生「java.util.Collections $ UnmodifiableMap」 這些都是我可以得到的。另外,該方法的參數是不可修改的。我一直都知道(它在文檔中),但我不確定我的代碼是否無意中嘗試修改該地圖。 感謝您的意見:-) – Darren

回答

2

副本Windows.Location.getParameterMap()中的一個HashMap,它會工作:

所以你發送新的HashMap>(Windows.Location.getParameterMap())通過RPC的作品。

問題是GWT的unmodifiableMap是不可序列化的。我知道它有一個Serializable標記,但是在GWT中它有點不同。大多數集合類都有自定義的GWT實現,有些不是100%兼容的。

+1

大衛你好。我想投票給你,但我不知道該怎麼做。 – Darren

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。

+0

感謝您看看這個安德烈亞斯。我將C&P代碼代替我的代碼,看看會發生什麼。 – Darren

+0

但是,如果出現錯誤,請不要留言給我 - 我在重構時沒有IDE;) –

+0

只有一些小的語法錯誤 - 在沒有IDE的情況下令人印象深刻。 仍會引發相同的錯誤。我只能看到這個錯誤,因爲這個方法是在一個異步GWT類中,當遠程執行調用失敗時(因爲我認爲拋出了異常),異常被捕獲,我輸出這個Throwable的消息。我想我需要找出如何讓這些例外顯示在App Engine日誌中。 非常感謝您的幫助:-) – Darren

0

做一個System.out.println(fbQueryStringParams.getClass());在郵件的開始處(或記錄它或任何你需要能夠看到它的內容)。

如果該參數從系統傳遞給您,它很可能會被封裝爲不可修改的集合,因爲它們不希望您改變它。

+0

感謝您花時間思考這款豆腐啤酒。 提供的Map絕對是不可修改的,因爲它代表了url查詢字符串中的參數,所以這些參數不應該被修改。 現在我很清楚(雖然有點笨拙),兩種編碼解決方案都不會修改地圖。 – Darren

+0

我有實際的問題,有用的日誌記錄工作。 根據http://code.google.com/appengine/docs/java/runtime.html#Logging上的GAE文檔, 「servlet寫入標準輸出流(System.out)的所有內容以及標準錯誤流(System.err)由App Engine捕獲並記錄在應用程序日誌中,寫入標準輸出流的行以「INFO」級別記錄,寫入標準錯誤流的行記錄在「WARNING」級別「。 但我的嘗試什麼都沒產生。我將不得不在GAE論壇上提出這個問題。 – Darren

+0

我真的相信你有一個客戶端錯誤。 –

0

我是否正確理解您在客戶端代碼中執行Window.Location.getParameterMap並將其發送給RPC調用中的服務器?在那種情況下...問題是:ParameterMap是可序列化的嗎? GWT並不是所有的實現都支持。所以它可能只是你的服務器代碼甚至沒有被調用,但它可以發送請求之前崩潰。你有沒有在GWT編譯期間看到任何警告?

該代碼儘管可以清理實現並確實可以擁有NPE,但不會修改提供的參數Map或Map值中的列表。所以問題可能在別的地方。

爲什麼不以託管模式(或稱爲GWT 2.0中的開發模式)運行應用程序?

大衛

+0

感謝您花時間在這個大衛身上。根據GWT源代碼,Window.Location.getParameterMap()返回一個java.util.HashMap,根據J2SE文檔實現Serializable接口。 – Darren

+0

至於託管模式,我不知道我明白你的意思。 我使用Eclipse的GAE/GWT插件。使用這個我可以在本地運行應用程序,代碼因其他原因失敗 - 主要是它沒有得到正確的FB url,但是我得到了完整的堆棧跟蹤。 如果你的意思是http://code.google.com/webtoolkit/doc/1.6/FAQ_DebuggingAndCompiling.html中的託管模式,那麼,blimey,設置這將是一個真正的挑戰,說實話,我不明白最它的:-( 必須有一種方法來獲取GAE日誌中顯示的異常和堆棧跟蹤 - 也許我應該先嚐試嗎? – Darren

+0

我查看了代碼,實際上。它正在構建一個HashMap,但最後它會返回Collections.unmodifiableMap。 如果您使用GAE/GWT插件,以託管模式運行其實非常簡單。只要在GWT項目上做一個運行爲Web應用程序。 你應該真的花時間在託管模式下運行,因爲這是開發的方式,更容易處理客戶端錯誤。如果客戶端發生崩潰,除非進行RPC調用,否則不能將其登錄到服務器上......這不是一個好主意。 –