Details

    • Type: Bug Bug
    • Status: Closed Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 4.1.2
    • Fix Version/s: 4.4-rc-1
    • Component/s: Old Core
    • Labels:
      None
    • Environment:
      JBoss AS7
    • Difficulty:
      Trivial
    • Documentation:
      N/A
    • Documentation in Release Notes:
      N/A
    • Similar issues:

      Description

      I suspect behaviour is related to JBOSS AS7 but may not be.

      When you request a xwiki page with parameters, even if the parameters are kepts the same, you have a cache miss. So page is rendered each time. Reason is this code, in DefaultRenderingCache

          private String getRequestParameters(XWikiContext context)
          {
              if (context.getRequest() != null) {
                  Map<String, String> parameters = context.getRequest().getParameterMap();
      
                  if (parameters != null) {
                      if (parameters.containsKey(PARAMETER_REFRESH)) {
                          parameters = new HashMap<String, String>(parameters);
      
                          parameters.remove(PARAMETER_REFRESH);
                      }
      
                      return parameters.toString();
                  }
              }
      
              return "";
          }
      

      It renders something like

      {menu=[Ljava.lang.String;@3e5da853, xpage=[Ljava.lang.String;@76d60121} 

      which is then used in the cache key along with page name and source code.
      This comes from the fact HttpServletRequest.getParameterMap() return String -> String[] association, event if parameters have only one value.

      Solution is to replace method with this code:

          private String getRequestParameters(XWikiContext context)
          {
              if (context.getRequest() != null) {
                  Map<String, Object> parameters = context.getRequest().getParameterMap();            
                  StringBuffer buffer = new StringBuffer(50);
                  buffer.append('{');
                  for (Map.Entry<String,Object> item : parameters.entrySet()){
                  	if (!PARAMETER_REFRESH.equals(item.getKey())){
      	            	buffer.append(item.getKey()).append('=');
      	            	if (item.getValue() instanceof Object[])
      	            		buffer.append(Arrays.deepToString((Object[])item.getValue()));
      	            	else
      	            		buffer.append(item.getValue());
                  	}
                  }
                  buffer.append('}');
                  return buffer.toString();
              }
      
              return "";
          }
      

      And then, no more problems.

        Activity

        Hide
        Sergiu Dumitriu added a comment -

        This is caused not by JBoss, but by the JDK implementation. The Oracle JDK provides a nice toString implementation for maps, but not all JDKs do. An alternative to your manual string computation is MapUtils.verbosePrint from commons-collections.

        Show
        Sergiu Dumitriu added a comment - This is caused not by JBoss, but by the JDK implementation. The Oracle JDK provides a nice toString implementation for maps, but not all JDKs do. An alternative to your manual string computation is MapUtils.verbosePrint from commons-collections.
        Hide
        David Delbecq added a comment -

        Sorry, but not, it's not the jdk, i am running on oracle JDK 1.7.0. And the code of oracle JDK's HashMap, for exemple, do not go inside nested arrays. It just uses the toString of every value.

        The problem is that the Cache code assume context.getRequest().getParameterMap() returns a Map<String,String> where you actually receive a Map<String,Object>, where Object can be String, String[] or whatever else (not sure what to do about "else" )

        And no, MapUtils is of no use:

        Map<String,Object> toTest = new HashMap<String,Object>();
        toTest.put("hello",new String[]{"World"});
        MapUtils.verbosePrint(System.out, null, toTest);
        

        Gives

        {
            hello = [Ljava.lang.String;@2f754ad2  //same problem
        }
        

        It does not go inside Arrays contained in the Map. It just use toString() on every item in the map, which is wrong because arrays do not have a proper toString() implementation.

        Here is the code of com.xpn.xwiki.web.XWikiServletRequest where you create the Map, as you can see, it's wrong to assume it just contains simple String as values:

            public Map getParameterMap()
            {
                Map newMap = new HashMap();
                Map map = this.request.getParameterMap();
                Iterator it = map.keySet().iterator();
                while (it.hasNext()) {
                    String key = (String) it.next();
                    Object value = map.get(key);
                    if (value instanceof String) {
                        newMap.put(key, filterString((String) value));
                    } else if (value instanceof String[]) {
                        newMap.put(key, filterStringArray((String[]) value));
                    } else {
                        newMap.put(key, value);
                    }
                }
                return map;
            }
        
        Show
        David Delbecq added a comment - Sorry, but not, it's not the jdk, i am running on oracle JDK 1.7.0. And the code of oracle JDK's HashMap, for exemple, do not go inside nested arrays. It just uses the toString of every value. The problem is that the Cache code assume context.getRequest().getParameterMap() returns a Map<String,String> where you actually receive a Map<String,Object>, where Object can be String, String[] or whatever else (not sure what to do about "else" ) And no, MapUtils is of no use: Map< String , Object > toTest = new HashMap< String , Object >(); toTest.put( "hello" , new String []{ "World" }); MapUtils.verbosePrint( System .out, null , toTest); Gives { hello = [Ljava.lang. String ;@2f754ad2 //same problem } It does not go inside Arrays contained in the Map. It just use toString() on every item in the map, which is wrong because arrays do not have a proper toString() implementation. Here is the code of com.xpn.xwiki.web.XWikiServletRequest where you create the Map, as you can see, it's wrong to assume it just contains simple String as values: public Map getParameterMap() { Map newMap = new HashMap(); Map map = this .request.getParameterMap(); Iterator it = map.keySet().iterator(); while (it.hasNext()) { String key = ( String ) it.next(); Object value = map.get(key); if (value instanceof String ) { newMap.put(key, filterString(( String ) value)); } else if (value instanceof String []) { newMap.put(key, filterStringArray(( String []) value)); } else { newMap.put(key, value); } } return map; }
        Hide
        David Delbecq added a comment -

        Additional funny fact, it returns 'map' where it should return 'newMap', do i have to fill an additional issue there?

        Show
        David Delbecq added a comment - Additional funny fact, it returns 'map' where it should return 'newMap', do i have to fill an additional issue there?
        Hide
        Sergiu Dumitriu added a comment -

        do i have to fill an additional issue there?

        Oh, yes, looks like it.

        Show
        Sergiu Dumitriu added a comment - do i have to fill an additional issue there? Oh, yes, looks like it.
        Hide
        Thomas Mortagne added a comment -

        Fixed by generating a query string from the Map.

        Show
        Thomas Mortagne added a comment - Fixed by generating a query string from the Map.

          People

          • Assignee:
            Thomas Mortagne
            Reporter:
            David Delbecq
          • Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:
              Date of First Response: