If you're interested in functional programming, you might also want to checkout my second blog which i'm actively working on!!

Tuesday, February 26, 2013

Debugging circular references in Maps

Today I learned another useful feature to debug circular references which can cause stackoverflows when not handled with caution. The below render method takes a parameter of type Map. In some cases the value is again a HashMap. Invoking entry.getValue() implicitly invokes the toString method in the Log.debug. When there are any circular references, you will get a nice stackoverflow.

    public String render(final String template,
            final Map<String, Object> parameters)
            throws IOException {

        final ST stringTemplate = new ST(template, '$', '$');

        if (parameters == null || parameters.isEmpty()) {
            LOG.warn("There are not any parameters passed to the template.");
        } else {
            for (Map.Entry<String, Object> entry : parameters.entrySet()) {
                stringTemplate.add(entry.getKey().replace(".", "_"),
                        (entry.getValue() instanceof String)
                        ? StringEscapeUtils.escapeXml(
                        entry.getValue().toString())
                        : entry.getValue());

                LOG.debug("Passing pipeline parameter as attribute: key={}"
                        + ", value={}", entry.getKey(), entry.getValue());
            }
        }

        return stringTemplate.render();
   

So you could e.g. write your own routine to find a clue where the problem is located but by following the Cocoon mailinglist I learned from Francesco that Commons Collections has a nice method to print a Map's content verbose. So I decided to experiment myself a bit to see how quickly I would find the issue using this approach and modified the code a bit.
for (Map.Entry<String, Object> entry : parameters.entrySet()) {
    stringTemplate.add(entry.getKey().replace(".", "_"),
        (entry.getValue() instanceof String)
         ? StringEscapeUtils.escapeXml(
         entry.getValue().toString())
         : entry.getValue());

     if (entry.getKey() == "cocoon")   MapUtils.verbosePrint(System.out, "cocoon", (Map) entry.getValue());
}

Now the output that got logged is listed below. I indented it a bit more properly but we can quickly see that "cocoon" has a "controller" which points back to "cocoon". --> (ancestor[0] Map)
cocoon = 
{
  response = org.apache.cocoon.servletservice.HttpServletResponseBufferingWrapper@12b13004
  settings = Settings:
      running mode : dev
      org.apache.cocoon.reload-delay : 1000
      org.apache.cocoon.reloading : false
      org.apache.cocoon.classloader.load.classes : 
      org.apache.cocoon.cache.directory : C:\workspaces\..\target\work\cache-dir
      org.apache.cocoon.work.directory : C:\workspaces\..\target\work
      org.apache.cocoon.formencoding : null
      org.apache.cocoon.containerencoding : UTF-8
  request = org.apache.cocoon.servlet.util.ObjectModelProvider$ObjectModelRequest@77d67cee
  context = org.apache.cocoon.servletservice.ServletServiceContext@7d91ec17
  controller = 
  {
    baseUrl = file:/C:/workspaces/apache/../src/main/resources/COB-INF/
    id = abc
    javax.servlet.http.HttpServletResponse = org.apache.cocoon.servletservice.HttpServletResponseBufferingWrapper@12b13004
    testProperty = test
    source = file:/C:/workspaces/apache/cocoon/cocoon3/trunk/cocoon-sample/src/main/resources/COB-INF/controller/demo.html
    javax.servlet.ServletContext = org.apache.cocoon.servletservice.ServletServiceContext@7d91ec17
    javax.servlet.http.HttpServletRequest = org.apache.cocoon.servletservice.util.ServletServiceRequest@5513fab7
    org.apache.cocoon.configuration.Settings = Settings:
        Running mode : dev
        org.apache.cocoon.reload-delay : 1000
        org.apache.cocoon.reloading : false
        org.apache.cocoon.classloader.load.classes : 
        org.apache.cocoon.cache.directory : C:\workspaces\..\target\work\cache-dir
        org.apache.cocoon.work.directory : C:\workspaces\..\target\work
        org.apache.cocoon.formencoding : null
        org.apache.cocoon.containerencoding : UTF-8
    name = foo
    cocoon = (ancestor[0] Map)
    reqparam = 1
  }
}


No comments:

Post a Comment