Save This Page
Home » struts-2.1.8.1-src » org.apache » struts2 » interceptor » debugging » [javadoc | source]
    1   /*
    2    * $Id: DebuggingInterceptor.java 651946 2008-04-27 13:41:38Z apetrelli $
    3    *
    4    * Licensed to the Apache Software Foundation (ASF) under one
    5    * or more contributor license agreements.  See the NOTICE file
    6    * distributed with this work for additional information
    7    * regarding copyright ownership.  The ASF licenses this file
    8    * to you under the Apache License, Version 2.0 (the
    9    * "License"); you may not use this file except in compliance
   10    * with the License.  You may obtain a copy of the License at
   11    *
   12    *  http://www.apache.org/licenses/LICENSE-2.0
   13    *
   14    * Unless required by applicable law or agreed to in writing,
   15    * software distributed under the License is distributed on an
   16    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   17    * KIND, either express or implied.  See the License for the
   18    * specific language governing permissions and limitations
   19    * under the License.
   20    */
   21   
   22   package org.apache.struts2.interceptor.debugging;
   23   
   24   import java.beans.BeanInfo;
   25   import java.beans.Introspector;
   26   import java.beans.PropertyDescriptor;
   27   import java.io.IOException;
   28   import java.io.PrintWriter;
   29   import java.io.StringWriter;
   30   import java.lang.reflect.Array;
   31   import java.lang.reflect.Method;
   32   import java.util.ArrayList;
   33   import java.util.Arrays;
   34   import java.util.Collection;
   35   import java.util.HashSet;
   36   import java.util.List;
   37   import java.util.Map;
   38   
   39   import javax.servlet.http.HttpServletResponse;
   40   
   41   import org.apache.struts2.ServletActionContext;
   42   import org.apache.struts2.StrutsConstants;
   43   import org.apache.struts2.views.freemarker.FreemarkerManager;
   44   import org.apache.struts2.views.freemarker.FreemarkerResult;
   45   
   46   import com.opensymphony.xwork2.ActionContext;
   47   import com.opensymphony.xwork2.ActionInvocation;
   48   import com.opensymphony.xwork2.inject.Inject;
   49   import com.opensymphony.xwork2.interceptor.Interceptor;
   50   import com.opensymphony.xwork2.interceptor.PreResultListener;
   51   import com.opensymphony.xwork2.util.ValueStack;
   52   import com.opensymphony.xwork2.util.logging.Logger;
   53   import com.opensymphony.xwork2.util.logging.LoggerFactory;
   54   import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
   55   
   56   /**
   57    * <!-- START SNIPPET: description -->
   58    * Provides several different debugging screens to provide insight into the
   59    * data behind the page.
   60    * <!-- END SNIPPET: description -->
   61    * The value of the 'debug' request parameter determines
   62    * the screen:
   63    * <!-- START SNIPPET: parameters -->
   64    * <ul>
   65    * <li> <code>xml</code> - Dumps the parameters, context, session, and value
   66    * stack as an XML document.</li>
   67    * <li> <code>console</code> - Shows a popup 'OGNL Console' that allows the
   68    * user to test OGNL expressions against the value stack. The XML data from
   69    * the 'xml' mode is inserted at the top of the page.</li>
   70    * <li> <code>command</code> - Tests an OGNL expression and returns the
   71    * string result. Only used by the OGNL console.</li>
   72    * <li><code>browser</code> Shows field values of an object specified in the 
   73    * <code>object<code> parameter (#context by default). When the <code>object<code>
   74    * parameters is set, the '#' character needs to be escaped to '%23'. Like
   75    * debug=browser&object=%23parameters</li>
   76    * </ul>
   77    * <!-- END SNIPPET: parameters -->
   78    * <p/>
   79    *  Example:
   80    * <!-- START SNIPPET: example -->
   81    *  http://localhost:8080/Welcome.action?debug=xml
   82    * <!-- END SNIPPET: example -->
   83    * <p/>
   84    * <!-- START SNIPPET: remarks -->
   85    * This interceptor only is activated when devMode is enabled in
   86    * struts.properties. The 'debug' parameter is removed from the parameter list
   87    * before the action is executed. All operations occur before the natural
   88    * Result has a chance to execute.
   89    * <!-- END SNIPPET: remarks -->
   90    */
   91   public class DebuggingInterceptor implements Interceptor {
   92   
   93       private static final long serialVersionUID = -3097324155953078783L;
   94   
   95       private final static Logger LOG = LoggerFactory.getLogger(DebuggingInterceptor.class);
   96   
   97       private String[] ignorePrefixes = new String[]{"org.apache.struts.",
   98               "com.opensymphony.xwork2.", "xwork."};
   99       private String[] _ignoreKeys = new String[]{"application", "session",
  100               "parameters", "request"};
  101       private HashSet<String> ignoreKeys = new HashSet<String>(Arrays.asList(_ignoreKeys));
  102   
  103       private final static String XML_MODE = "xml";
  104       private final static String CONSOLE_MODE = "console";
  105       private final static String COMMAND_MODE = "command";
  106       private final static String BROWSER_MODE = "browser";
  107   
  108       private final static String SESSION_KEY = "org.apache.struts2.interceptor.debugging.VALUE_STACK";
  109   
  110       private final static String DEBUG_PARAM = "debug";
  111       private final static String OBJECT_PARAM = "object";
  112       private final static String EXPRESSION_PARAM = "expression";
  113       private final static String DECORATE_PARAM = "decorate";
  114   
  115       private boolean enableXmlWithConsole = false;
  116       
  117       private boolean devMode;
  118       private FreemarkerManager freemarkerManager;
  119       
  120       private boolean consoleEnabled = false;
  121       private ReflectionProvider reflectionProvider;
  122   
  123       @Inject(StrutsConstants.STRUTS_DEVMODE)
  124       public void setDevMode(String mode) {
  125           this.devMode = "true".equals(mode);
  126       }
  127       
  128       @Inject
  129       public void setFreemarkerManager(FreemarkerManager mgr) {
  130           this.freemarkerManager = mgr;
  131       }
  132       
  133       @Inject
  134       public void setReflectionProvider(ReflectionProvider reflectionProvider) {
  135           this.reflectionProvider = reflectionProvider;
  136       }
  137   
  138       /**
  139        * Unused.
  140        */
  141       public void init() {
  142       }
  143   
  144   
  145       /**
  146        * Unused.
  147        */
  148       public void destroy() {
  149       }
  150   
  151   
  152       /*
  153        * (non-Javadoc)
  154        *
  155        * @see com.opensymphony.xwork2.interceptor.Interceptor#invoke(com.opensymphony.xwork2.ActionInvocation)
  156        */
  157       public String intercept(ActionInvocation inv) throws Exception {
  158           boolean actionOnly = false;
  159           boolean cont = true;
  160           if (devMode) {
  161               final ActionContext ctx = ActionContext.getContext();
  162               String type = getParameter(DEBUG_PARAM);
  163               ctx.getParameters().remove(DEBUG_PARAM);
  164               if (XML_MODE.equals(type)) {
  165                   inv.addPreResultListener(
  166                           new PreResultListener() {
  167                               public void beforeResult(ActionInvocation inv, String result) {
  168                                   printContext();
  169                               }
  170                           });
  171               } else if (CONSOLE_MODE.equals(type)) {
  172                   consoleEnabled = true;
  173                   inv.addPreResultListener(
  174                           new PreResultListener() {
  175                               public void beforeResult(ActionInvocation inv, String actionResult) {
  176                                   String xml = "";
  177                                   if (enableXmlWithConsole) {
  178                                       StringWriter writer = new StringWriter();
  179                                       printContext(new PrettyPrintWriter(writer));
  180                                       xml = writer.toString();
  181                                       xml = xml.replaceAll("&", "&amp;");
  182                                       xml = xml.replaceAll(">", "&gt;");
  183                                       xml = xml.replaceAll("<", "&lt;");
  184                                   }
  185                                   ActionContext.getContext().put("debugXML", xml);
  186   
  187                                   FreemarkerResult result = new FreemarkerResult();
  188                                   result.setFreemarkerManager(freemarkerManager);
  189                                   result.setContentType("text/html");
  190                                   result.setLocation("/org/apache/struts2/interceptor/debugging/console.ftl");
  191                                   result.setParse(false);
  192                                   try {
  193                                       result.execute(inv);
  194                                   } catch (Exception ex) {
  195                                       LOG.error("Unable to create debugging console", ex);
  196                                   }
  197   
  198                               }
  199                           });
  200               } else if (COMMAND_MODE.equals(type)) {
  201                   ValueStack stack = (ValueStack) ctx.getSession().get(SESSION_KEY);
  202                   if (stack == null) {
  203                       //allows it to be embedded on another page
  204                       stack = (ValueStack) ctx.get(ActionContext.VALUE_STACK);
  205                       ctx.getSession().put(SESSION_KEY, stack);
  206                   }
  207                   String cmd = getParameter(EXPRESSION_PARAM);
  208   
  209                   ServletActionContext.getRequest().setAttribute("decorator", "none");
  210                   HttpServletResponse res = ServletActionContext.getResponse();
  211                   res.setContentType("text/plain");
  212   
  213                   try {
  214                       PrintWriter writer =
  215                               ServletActionContext.getResponse().getWriter();
  216                       writer.print(stack.findValue(cmd));
  217                       writer.close();
  218                   } catch (IOException ex) {
  219                       ex.printStackTrace();
  220                   }
  221                   cont = false;
  222               } else if (BROWSER_MODE.equals(type)) {
  223                   actionOnly = true;
  224                   inv.addPreResultListener(
  225                       new PreResultListener() {
  226                           public void beforeResult(ActionInvocation inv, String actionResult) {
  227                               String rootObjectExpression = getParameter(OBJECT_PARAM);
  228                               if (rootObjectExpression == null)
  229                                   rootObjectExpression = "#context";
  230                               String decorate = getParameter(DECORATE_PARAM);
  231                               ValueStack stack = (ValueStack) ctx.get(ActionContext.VALUE_STACK);
  232                               Object rootObject = stack.findValue(rootObjectExpression);
  233                               
  234                               try {
  235                                   StringWriter writer = new StringWriter();
  236                                   ObjectToHTMLWriter htmlWriter = new ObjectToHTMLWriter(writer);
  237                                   htmlWriter.write(reflectionProvider, rootObject, rootObjectExpression);
  238                                   String html = writer.toString();
  239                                   writer.close();
  240                                   
  241                                   stack.set("debugHtml", html);
  242                                   
  243                                   //on the first request, response can be decorated
  244                                   //but we need plain text on the other ones
  245                                   if ("false".equals(decorate))
  246                                       ServletActionContext.getRequest().setAttribute("decorator", "none");
  247                                   
  248                                   FreemarkerResult result = new FreemarkerResult();
  249                                   result.setFreemarkerManager(freemarkerManager);
  250                                   result.setContentType("text/html");
  251                                   result.setLocation("/org/apache/struts2/interceptor/debugging/browser.ftl");
  252                                   result.execute(inv);
  253                               } catch (Exception ex) {
  254                                   LOG.error("Unable to create debugging console", ex);
  255                               }
  256   
  257                           }
  258                       });
  259               }
  260           } 
  261           if (cont) {
  262               try {
  263                   if (actionOnly) {
  264                       inv.invokeActionOnly();
  265                       return null;
  266                   } else {
  267                       return inv.invoke();
  268                   }
  269               } finally {
  270                   if (devMode && consoleEnabled) {
  271                       final ActionContext ctx = ActionContext.getContext();
  272                       ctx.getSession().put(SESSION_KEY, ctx.get(ActionContext.VALUE_STACK));
  273                   }
  274               }
  275           } else {
  276               return null;
  277           }
  278       }
  279   
  280   
  281       /**
  282        * Gets a single string from the request parameters
  283        *
  284        * @param key The key
  285        * @return The parameter value
  286        */
  287       private String getParameter(String key) {
  288           String[] arr = (String[]) ActionContext.getContext().getParameters().get(key);
  289           if (arr != null && arr.length > 0) {
  290               return arr[0];
  291           }
  292           return null;
  293       }
  294   
  295   
  296       /**
  297        * Prints the current context to the response in XML format.
  298        */
  299       protected void printContext() {
  300           HttpServletResponse res = ServletActionContext.getResponse();
  301           res.setContentType("text/xml");
  302   
  303           try {
  304               PrettyPrintWriter writer = new PrettyPrintWriter(
  305                       ServletActionContext.getResponse().getWriter());
  306               printContext(writer);
  307               writer.close();
  308           } catch (IOException ex) {
  309               ex.printStackTrace();
  310           }
  311       }
  312   
  313   
  314       /**
  315        * Prints the current request to the existing writer.
  316        *
  317        * @param writer The XML writer
  318        */
  319       protected void printContext(PrettyPrintWriter writer) {
  320           ActionContext ctx = ActionContext.getContext();
  321           writer.startNode(DEBUG_PARAM);
  322           serializeIt(ctx.getParameters(), "parameters", writer,
  323                   new ArrayList<Object>());
  324           writer.startNode("context");
  325           String key;
  326           Map ctxMap = ctx.getContextMap();
  327           for (Object o : ctxMap.keySet()) {
  328               key = o.toString();
  329               boolean print = !ignoreKeys.contains(key);
  330   
  331               for (String ignorePrefixe : ignorePrefixes) {
  332                   if (key.startsWith(ignorePrefixe)) {
  333                       print = false;
  334                       break;
  335                   }
  336               }
  337               if (print) {
  338                   serializeIt(ctxMap.get(key), key, writer, new ArrayList<Object>());
  339               }
  340           }
  341           writer.endNode();
  342           Map requestMap = (Map) ctx.get("request");
  343           serializeIt(requestMap, "request", writer, filterValueStack(requestMap));
  344           serializeIt(ctx.getSession(), "session", writer, new ArrayList<Object>());
  345   
  346           ValueStack stack = (ValueStack) ctx.get(ActionContext.VALUE_STACK);
  347           serializeIt(stack.getRoot(), "valueStack", writer, new ArrayList<Object>());
  348           writer.endNode();
  349       }
  350   
  351   
  352       /**
  353        * Recursive function to serialize objects to XML. Currently it will
  354        * serialize Collections, maps, Arrays, and JavaBeans. It maintains a stack
  355        * of objects serialized already in the current functioncall. This is used
  356        * to avoid looping (stack overflow) of circular linked objects. Struts and
  357        * XWork objects are ignored.
  358        *
  359        * @param bean   The object you want serialized.
  360        * @param name   The name of the object, used for element &lt;name/&gt;
  361        * @param writer The XML writer
  362        * @param stack  List of objects we're serializing since the first calling
  363        *               of this function (to prevent looping on circular references).
  364        */
  365       protected void serializeIt(Object bean, String name,
  366                                  PrettyPrintWriter writer, List<Object> stack) {
  367           writer.flush();
  368           // Check stack for this object
  369           if ((bean != null) && (stack.contains(bean))) {
  370               if (LOG.isInfoEnabled()) {
  371                   LOG.info("Circular reference detected, not serializing object: "
  372                           + name);
  373               }
  374               return;
  375           } else if (bean != null) {
  376               // Push object onto stack.
  377               // Don't push null objects ( handled below)
  378               stack.add(bean);
  379           }
  380           if (bean == null) {
  381               return;
  382           }
  383           String clsName = bean.getClass().getName();
  384   
  385           writer.startNode(name);
  386   
  387           // It depends on the object and it's value what todo next:
  388           if (bean instanceof Collection) {
  389               Collection col = (Collection) bean;
  390   
  391               // Iterate through components, and call ourselves to process
  392               // elements
  393               for (Object aCol : col) {
  394                   serializeIt(aCol, "value", writer, stack);
  395               }
  396           } else if (bean instanceof Map) {
  397   
  398               Map<Object, Object> map = (Map) bean;
  399   
  400               // Loop through keys and call ourselves
  401               for (Map.Entry<Object, Object> entry : map.entrySet()) {
  402                   Object objValue = entry.getValue();
  403                   serializeIt(objValue, entry.getKey().toString(), writer, stack);
  404               }
  405           } else if (bean.getClass().isArray()) {
  406               // It's an array, loop through it and keep calling ourselves
  407               for (int i = 0; i < Array.getLength(bean); i++) {
  408                   serializeIt(Array.get(bean, i), "arrayitem", writer, stack);
  409               }
  410           } else {
  411               if (clsName.startsWith("java.lang")) {
  412                   writer.setValue(bean.toString());
  413               } else {
  414                   // Not java.lang, so we can call ourselves with this object's
  415                   // values
  416                   try {
  417                       BeanInfo info = Introspector.getBeanInfo(bean.getClass());
  418                       PropertyDescriptor[] props = info.getPropertyDescriptors();
  419   
  420                       for (PropertyDescriptor prop : props) {
  421                           String n = prop.getName();
  422                           Method m = prop.getReadMethod();
  423   
  424                           // Call ourselves with the result of the method
  425                           // invocation
  426                           if (m != null) {
  427                               serializeIt(m.invoke(bean), n, writer, stack);
  428                           }
  429                       }
  430                   } catch (Exception e) {
  431                       LOG.error(e.toString(), e);
  432                   }
  433               }
  434           }
  435   
  436           writer.endNode();
  437   
  438           // Remove object from stack
  439           stack.remove(bean);
  440       }
  441   
  442   
  443       /**
  444        * @param enableXmlWithConsole the enableXmlWithConsole to set
  445        */
  446       public void setEnableXmlWithConsole(boolean enableXmlWithConsole) {
  447           this.enableXmlWithConsole = enableXmlWithConsole;
  448       }
  449   
  450       
  451       private List<Object> filterValueStack(Map requestMap) {
  452       	List<Object> filter = new ArrayList<Object>();
  453       	Object valueStack = requestMap.get("struts.valueStack");
  454       	if(valueStack != null) {
  455       		filter.add(valueStack);
  456       	}
  457       	return filter;
  458       }
  459   
  460   
  461   }
  462   
  463   

Save This Page
Home » struts-2.1.8.1-src » org.apache » struts2 » interceptor » debugging » [javadoc | source]