Save This Page
Home » struts-2.0.11.2-src » org.apache » struts2 » dispatcher » mapper » [javadoc | source]
    1   /*
    2    * $Id: DefaultActionMapper.java 540141 2007-05-21 13:46:48Z mrdon $
    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   package org.apache.struts2.dispatcher.mapper;
   22   
   23   import java.util.ArrayList;
   24   import java.util.Arrays;
   25   import java.util.HashSet;
   26   import java.util.Iterator;
   27   import java.util.List;
   28   import java.util.Map;
   29   import java.util.Set;
   30   
   31   import javax.servlet.http.HttpServletRequest;
   32   
   33   import org.apache.struts2.RequestUtils;
   34   import org.apache.struts2.StrutsConstants;
   35   import org.apache.struts2.dispatcher.ServletRedirectResult;
   36   import org.apache.struts2.util.PrefixTrie;
   37   
   38   import com.opensymphony.xwork2.config.Configuration;
   39   import com.opensymphony.xwork2.config.ConfigurationManager;
   40   import com.opensymphony.xwork2.config.entities.PackageConfig;
   41   import com.opensymphony.xwork2.inject.Inject;
   42   import com.opensymphony.xwork2.inject.Container;
   43   
   44   /**
   45    * <!-- START SNIPPET: javadoc -->
   46    *
   47    * Default action mapper implementation, using the standard *.[ext] (where ext
   48    * usually "action") pattern. The extension is looked up from the Struts
   49    * configuration key <b>struts.action.exection</b>.
   50    *
   51    * <p/> To help with dealing with buttons and other related requirements, this
   52    * mapper (and other {@link ActionMapper}s, we hope) has the ability to name a
   53    * button with some predefined prefix and have that button name alter the
   54    * execution behaviour. The four prefixes are:
   55    *
   56    * <ul>
   57    *
   58    * <li>Method prefix - <i>method:default</i></li>
   59    *
   60    * <li>Action prefix - <i>action:dashboard</i></li>
   61    *
   62    * <li>Redirect prefix - <i>redirect:cancel.jsp</i></li>
   63    *
   64    * <li>Redirect-action prefix - <i>redirect-action:cancel</i></li>
   65    *
   66    * </ul>
   67    *
   68    * <p/> In addition to these four prefixes, this mapper also understands the
   69    * action naming pattern of <i>foo!bar</i> in either the extension form (eg:
   70    * foo!bar.action) or in the prefix form (eg: action:foo!bar). This syntax tells
   71    * this mapper to map to the action named <i>foo</i> and the method <i>bar</i>.
   72    *
   73    * <!-- END SNIPPET: javadoc -->
   74    *
   75    * <p/> <b>Method Prefix</b> <p/>
   76    *
   77    * <!-- START SNIPPET: method -->
   78    *
   79    * With method-prefix, instead of calling baz action's execute() method (by
   80    * default if it isn't overriden in struts.xml to be something else), the baz
   81    * action's anotherMethod() will be called. A very elegant way determine which
   82    * button is clicked. Alternatively, one would have submit button set a
   83    * particular value on the action when clicked, and the execute() method decides
   84    * on what to do with the setted value depending on which button is clicked.
   85    *
   86    * <!-- END SNIPPET: method -->
   87    *
   88    * <pre>
   89    *  &lt;!-- START SNIPPET: method-example --&gt;
   90    *  &lt;a:form action=&quot;baz&quot;&gt;
   91    *      &lt;a:textfield label=&quot;Enter your name&quot; name=&quot;person.name&quot;/&gt;
   92    *      &lt;a:submit value=&quot;Create person&quot;/&gt;
   93    *      &lt;a:submit name=&quot;method:anotherMethod&quot; value=&quot;Cancel&quot;/&gt;
   94    *  &lt;/a:form&gt;
   95    *  &lt;!-- END SNIPPET: method-example --&gt;
   96    * </pre>
   97    *
   98    * <p/> <b>Action prefix</b> <p/>
   99    *
  100    * <!-- START SNIPPET: action -->
  101    *
  102    * With action-prefix, instead of executing baz action's execute() method (by
  103    * default if it isn't overriden in struts.xml to be something else), the
  104    * anotherAction action's execute() method (assuming again if it isn't overriden
  105    * with something else in struts.xml) will be executed.
  106    *
  107    * <!-- END SNIPPET: action -->
  108    *
  109    * <pre>
  110    *  &lt;!-- START SNIPPET: action-example --&gt;
  111    *  &lt;a:form action=&quot;baz&quot;&gt;
  112    *      &lt;a:textfield label=&quot;Enter your name&quot; name=&quot;person.name&quot;/&gt;
  113    *      &lt;a:submit value=&quot;Create person&quot;/&gt;
  114    *      &lt;a:submit name=&quot;action:anotherAction&quot; value=&quot;Cancel&quot;/&gt;
  115    *  &lt;/a:form&gt;
  116    *  &lt;!-- END SNIPPET: action-example --&gt;
  117    * </pre>
  118    *
  119    * <p/> <b>Redirect prefix</b> <p/>
  120    *
  121    * <!-- START SNIPPET: redirect -->
  122    *
  123    * With redirect-prefix, instead of executing baz action's execute() method (by
  124    * default it isn't overriden in struts.xml to be something else), it will get
  125    * redirected to, in this case to www.google.com. Internally it uses
  126    * ServletRedirectResult to do the task.
  127    *
  128    * <!-- END SNIPPET: redirect -->
  129    *
  130    * <pre>
  131    *  &lt;!-- START SNIPPET: redirect-example --&gt;
  132    *  &lt;a:form action=&quot;baz&quot;&gt;
  133    *      &lt;a:textfield label=&quot;Enter your name&quot; name=&quot;person.name&quot;/&gt;
  134    *      &lt;a:submit value=&quot;Create person&quot;/&gt;
  135    *      &lt;a:submit name=&quot;redirect:www.google.com&quot; value=&quot;Cancel&quot;/&gt;
  136    *  &lt;/a:form&gt;
  137    *  &lt;!-- END SNIPPET: redirect-example --&gt;
  138    * </pre>
  139    *
  140    * <p/> <b>Redirect-action prefix</b> <p/>
  141    *
  142    * <!-- START SNIPPET: redirect-action -->
  143    *
  144    * With redirect-action-prefix, instead of executing baz action's execute()
  145    * method (by default it isn't overriden in struts.xml to be something else), it
  146    * will get redirected to, in this case 'dashboard.action'. Internally it uses
  147    * ServletRedirectResult to do the task and read off the extension from the
  148    * struts.properties.
  149    *
  150    * <!-- END SNIPPET: redirect-action -->
  151    *
  152    * <pre>
  153    *  &lt;!-- START SNIPPET: redirect-action-example --&gt;
  154    *  &lt;a:form action=&quot;baz&quot;&gt;
  155    *      &lt;a:textfield label=&quot;Enter your name&quot; name=&quot;person.name&quot;/&gt;
  156    *      &lt;a:submit value=&quot;Create person&quot;/&gt;
  157    *      &lt;a:submit name=&quot;redirect-action:dashboard&quot; value=&quot;Cancel&quot;/&gt;
  158    *  &lt;/a:form&gt;
  159    *  &lt;!-- END SNIPPET: redirect-action-example --&gt;
  160    * </pre>
  161    *
  162    */
  163   public class DefaultActionMapper implements ActionMapper {
  164   
  165       static final String METHOD_PREFIX = "method:";
  166   
  167       static final String ACTION_PREFIX = "action:";
  168   
  169       static final String REDIRECT_PREFIX = "redirect:";
  170   
  171       static final String REDIRECT_ACTION_PREFIX = "redirect-action:";
  172   
  173       private boolean allowDynamicMethodCalls = true;
  174   
  175       private boolean allowSlashesInActionNames = false;
  176       
  177       private boolean alwaysSelectFullNamespace = false;
  178   
  179       private PrefixTrie prefixTrie = null;
  180       
  181       List extensions = new ArrayList() {{ add("action");}};
  182   
  183       private Container container;
  184   
  185       public DefaultActionMapper() {
  186           prefixTrie = new PrefixTrie() {
  187               {
  188                   put(METHOD_PREFIX, new ParameterAction() {
  189                       public void execute(String key, ActionMapping mapping) {
  190                           mapping
  191                                   .setMethod(key
  192                                           .substring(METHOD_PREFIX.length()));
  193                       }
  194                   });
  195   
  196                   put(ACTION_PREFIX, new ParameterAction() {
  197                       public void execute(String key, ActionMapping mapping) {
  198                           String name = key.substring(ACTION_PREFIX.length());
  199                           if (allowDynamicMethodCalls) {
  200                               int bang = name.indexOf('!');
  201                               if (bang != -1) {
  202                                   String method = name.substring(bang + 1);
  203                                   mapping.setMethod(method);
  204                                   name = name.substring(0, bang);
  205                               }
  206                           }
  207                           mapping.setName(name);
  208                       }
  209                   });
  210   
  211                   put(REDIRECT_PREFIX, new ParameterAction() {
  212                       public void execute(String key, ActionMapping mapping) {
  213                           ServletRedirectResult redirect = new ServletRedirectResult();
  214                           container.inject(redirect);
  215                           redirect.setLocation(key.substring(REDIRECT_PREFIX
  216                                   .length()));
  217                           mapping.setResult(redirect);
  218                       }
  219                   });
  220   
  221                   put(REDIRECT_ACTION_PREFIX, new ParameterAction() {
  222                       public void execute(String key, ActionMapping mapping) {
  223                           String location = key.substring(REDIRECT_ACTION_PREFIX
  224                                   .length());
  225                           ServletRedirectResult redirect = new ServletRedirectResult();
  226                           container.inject(redirect);
  227                           String extension = getDefaultExtension();
  228                           if (extension != null) {
  229                               location += "." + extension;
  230                           }
  231                           redirect.setLocation(location);
  232                           mapping.setResult(redirect);
  233                       }
  234                   });
  235               }
  236           };
  237       }
  238       
  239       @Inject(StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION)
  240       public void setAllowDynamicMethodCalls(String allow) {
  241           allowDynamicMethodCalls = "true".equals(allow);
  242       }
  243       
  244       @Inject(StrutsConstants.STRUTS_ENABLE_SLASHES_IN_ACTION_NAMES)
  245       public void setSlashesInActionNames(String allow) {
  246           allowSlashesInActionNames = "true".equals(allow);
  247       }
  248       
  249       @Inject(StrutsConstants.STRUTS_ALWAYS_SELECT_FULL_NAMESPACE)
  250       public void setAlwaysSelectFullNamespace(String val) {
  251           this.alwaysSelectFullNamespace = "true".equals(val);
  252       }
  253   
  254       @Inject
  255       public void setContainer(Container container) {
  256           this.container = container;
  257       }
  258   
  259       /*
  260        * (non-Javadoc)
  261        *
  262        * @see org.apache.struts2.dispatcher.mapper.ActionMapper#getMapping(javax.servlet.http.HttpServletRequest)
  263        */
  264       public ActionMapping getMapping(HttpServletRequest request,
  265               ConfigurationManager configManager) {
  266           ActionMapping mapping = new ActionMapping();
  267           String uri = getUri(request);
  268   
  269           uri = dropExtension(uri);
  270           if (uri == null) {
  271               return null;
  272           }
  273   
  274           parseNameAndNamespace(uri, mapping, configManager);
  275   
  276           handleSpecialParameters(request, mapping);
  277   
  278           if (mapping.getName() == null) {
  279               return null;
  280           }
  281   
  282           if (allowDynamicMethodCalls) {
  283               // handle "name!method" convention.
  284               String name = mapping.getName();
  285               int exclamation = name.lastIndexOf("!");
  286               if (exclamation != -1) {
  287                   mapping.setName(name.substring(0, exclamation));
  288                   mapping.setMethod(name.substring(exclamation + 1));
  289               }
  290           }
  291   
  292           return mapping;
  293       }
  294   
  295       /**
  296        * Special parameters, as described in the class-level comment, are searched
  297        * for and handled.
  298        *
  299        * @param request
  300        *            The request
  301        * @param mapping
  302        *            The action mapping
  303        */
  304       public void handleSpecialParameters(HttpServletRequest request,
  305               ActionMapping mapping) {
  306           // handle special parameter prefixes.
  307           Set<String> uniqueParameters = new HashSet<String>();
  308           Map parameterMap = request.getParameterMap();
  309           for (Iterator iterator = parameterMap.keySet().iterator(); iterator
  310                   .hasNext();) {
  311               String key = (String) iterator.next();
  312               
  313               // Strip off the image button location info, if found
  314               if (key.endsWith(".x") || key.endsWith(".y")) {
  315                   key = key.substring(0, key.length() - 2);
  316               }
  317               
  318               // Ensure a parameter doesn't get processed twice
  319               if (!uniqueParameters.contains(key)) {
  320                   ParameterAction parameterAction = (ParameterAction) prefixTrie
  321                           .get(key);
  322                   if (parameterAction != null) {
  323                       parameterAction.execute(key, mapping);
  324                       uniqueParameters.add(key);
  325                       break;
  326                   }
  327               }
  328           }
  329       }
  330   
  331       /**
  332        * Parses the name and namespace from the uri
  333        *
  334        * @param uri
  335        *            The uri
  336        * @param mapping
  337        *            The action mapping to populate
  338        */
  339       void parseNameAndNamespace(String uri, ActionMapping mapping,
  340               ConfigurationManager configManager) {
  341           String namespace, name;
  342           int lastSlash = uri.lastIndexOf("/");
  343           if (lastSlash == -1) {
  344               namespace = "";
  345               name = uri;
  346           } else if (lastSlash == 0) {
  347               // ww-1046, assume it is the root namespace, it will fallback to
  348               // default
  349               // namespace anyway if not found in root namespace.
  350               namespace = "/";
  351               name = uri.substring(lastSlash + 1);
  352           } else if (alwaysSelectFullNamespace) {
  353               // Simply select the namespace as everything before the last slash
  354               namespace = uri.substring(0, lastSlash);
  355               name = uri.substring(lastSlash + 1);
  356           } else {
  357               // Try to find the namespace in those defined, defaulting to ""
  358               Configuration config = configManager.getConfiguration();
  359               String prefix = uri.substring(0, lastSlash);
  360               namespace = "";
  361               // Find the longest matching namespace, defaulting to the default
  362               for (Iterator i = config.getPackageConfigs().values().iterator(); i
  363                       .hasNext();) {
  364                   String ns = ((PackageConfig) i.next()).getNamespace();
  365                   if (ns != null && prefix.startsWith(ns) && (prefix.length() == ns.length() || prefix.charAt(ns.length()) == '/')) {
  366                       if (ns.length() > namespace.length()) {
  367                           namespace = ns;
  368                       }
  369                   }
  370               }
  371   
  372               name = uri.substring(namespace.length() + 1);
  373           }
  374   
  375           if (!allowSlashesInActionNames && name != null) {
  376               int pos = name.lastIndexOf('/');
  377               if (pos > -1 && pos < name.length() - 1) {
  378                   name = name.substring(pos + 1);
  379               }
  380           }
  381   
  382           mapping.setNamespace(namespace);
  383           mapping.setName(name);
  384       }
  385   
  386       /**
  387        * Drops the extension from the action name
  388        *
  389        * @param name
  390        *            The action name
  391        * @return The action name without its extension
  392        */
  393       String dropExtension(String name) {
  394           if (extensions == null) {
  395               return name;
  396           }
  397           Iterator it = extensions.iterator();
  398           while (it.hasNext()) {
  399               String extension = "." + (String) it.next();
  400               if (name.endsWith(extension)) {
  401                   name = name.substring(0, name.length() - extension.length());
  402                   return name;
  403               }
  404           }
  405           return null;
  406       }
  407   
  408       /**
  409        * Returns null if no extension is specified.
  410        */
  411       String getDefaultExtension() {
  412           if (extensions == null) {
  413               return null;
  414           } else {
  415               return (String) extensions.get(0);
  416           }
  417       }
  418   
  419       @Inject(StrutsConstants.STRUTS_ACTION_EXTENSION)
  420       public void setExtensions(String extensions) {
  421           if (!"".equals(extensions)) {
  422               this.extensions = Arrays.asList(extensions.split(","));
  423           } else {
  424               this.extensions = null;
  425           }
  426       }
  427       
  428       /**
  429        * Gets the uri from the request
  430        *
  431        * @param request
  432        *            The request
  433        * @return The uri
  434        */
  435       String getUri(HttpServletRequest request) {
  436           // handle http dispatcher includes.
  437           String uri = (String) request
  438                   .getAttribute("javax.servlet.include.servlet_path");
  439           if (uri != null) {
  440               return uri;
  441           }
  442   
  443           uri = RequestUtils.getServletPath(request);
  444           if (uri != null && !"".equals(uri)) {
  445               return uri;
  446           }
  447   
  448           uri = request.getRequestURI();
  449           return uri.substring(request.getContextPath().length());
  450       }
  451   
  452       /*
  453        * (non-Javadoc)
  454        *
  455        * @see org.apache.struts2.dispatcher.mapper.ActionMapper#getUriFromActionMapping(org.apache.struts2.dispatcher.mapper.ActionMapping)
  456        */
  457       public String getUriFromActionMapping(ActionMapping mapping) {
  458           StringBuffer uri = new StringBuffer();
  459   
  460           uri.append(mapping.getNamespace());
  461           if (!"/".equals(mapping.getNamespace())) {
  462               uri.append("/");
  463           }
  464           String name = mapping.getName();
  465           String params = "";
  466           if (name.indexOf('?') != -1) {
  467               params = name.substring(name.indexOf('?'));
  468               name = name.substring(0, name.indexOf('?'));
  469           }
  470           uri.append(name);
  471   
  472           if (null != mapping.getMethod() && !"".equals(mapping.getMethod())) {
  473               uri.append("!").append(mapping.getMethod());
  474           }
  475   
  476           String extension = getDefaultExtension();
  477           if (extension != null) {
  478               if (uri.indexOf('.' + extension) == -1) {
  479                   uri.append(".").append(extension);
  480                   if (params.length() > 0) {
  481                       uri.append(params);
  482                   }
  483               }
  484           }
  485   
  486           return uri.toString();
  487       }
  488       
  489   
  490   	public boolean isSlashesInActionNames() {
  491   		return allowSlashesInActionNames;
  492   	}
  493   	
  494   	/**
  495        * Defines a parameter action prefix
  496        */
  497       interface ParameterAction {
  498           void execute(String key, ActionMapping mapping);
  499       }
  500   
  501   }

Save This Page
Home » struts-2.0.11.2-src » org.apache » struts2 » dispatcher » mapper » [javadoc | source]