Announcement Announcement Module
Collapse
No announcement yet.
Discovering controller's mapping name within controller? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Discovering controller's mapping name within controller?

    If I have an URL which looks like http://mydomain/myapp/action/proxy/p...me/path/blabla, and myapp is my application, action is DispatcherServlet's mapping and /proxy/perform/** is a simple url mapping, how can I discover within the controller that that particular request is triggered by resolving /proxy/perform mapping, and that /some/path/blabla is actually a parameter?

    This is my current configuration and I'm a little bit stuck here. Basically, my proxy controller maps itself to a file system (local or remote), or maybe carries a set of variable-length parameters, which must be represented as shown above. I'm having troubles discovering where the parameters start, or more preciselly, where spring ends and the parameters start.

    So, in short :-) How to discover the current mapping of the controller within its handleRequest method? Is there some already available Controller implementation which provides me with that information?

    Thanks.

  • #2
    I had the same problem since I'm also trying to avoid to use the queryString since this gives better Google rankings and crawling. Below some code I use, the idea is basically that in my categoryController, I call getExtractor().getUriParams(request), which gives me a Map with the parameters.
    In the example below, a call to getExtractor().getUriParams().get("categoryId") gives me 123 for the url http://localhost/nl/category/123/index.html.
    You can specify a comma separated list of parameters to categoryControllerUriParamExtractor.params, as long as you specify as many match groups with "(.*)" in your regular expression.

    Code:
    <!-- URL Mappings and interceptors -->
    <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    	<property name="alwaysUseFullPath"><value>true</value></property>
    	<!-- The mappings all start with the locale in front -->
    	<property name="mappings">
    		<props>
    			<prop key="/*/category/*/index.html">categoryController</prop>				
    		</props>
    	</property>
    </bean>
    Code:
    <!-- Category Controller -->
    <bean id="categoryController" class="com.onthoo.web.controllers.CategoryController">
    	<property name="extractor"><ref bean="categoryControllerUriParamExtractor"/></property>
    	<property name="viewName"><value>agentCategoryView</value></property>
    </bean>
    <bean id="categoryControllerUriParamExtractor" class="com.onthoo.web.controllers.URIParameterExtractor">
    	<property name="patternString"><value><!&#91;CDATA&#91;/&#91;^/&#93;*/category/&#40;&#91;^/&#93;*&#41;/index.html&#93;&#93;></value></property>
    	<property name="params"><value>categoryId</value></property>
    </bean>

    Code:
    package com.onthoo.web.controllers;
    
    import java.util.Properties;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.beans.factory.InitializingBean;
    
    import com.onthoo.business.exception.ConfigException;
    import com.onthoo.business.exception.OnthooApplicationException;
    import com.onthoo.business.properties.ValidatorHelper;
    
    
    /**
     * @author Pieter Coucke
     */
    public class URIParameterExtractor implements InitializingBean &#123;
        
        private static Log log = LogFactory.getLog&#40;URIParameterExtractor.class&#41;;
        
        private Pattern pattern;
        private String&#91;&#93; params;
        
        
        /**
         * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet&#40;&#41;
         */
        public void afterPropertiesSet&#40;&#41; throws ConfigException &#123;
            ValidatorHelper.assertNotNull&#40;"pattern", getPattern&#40;&#41;&#41;;
            ValidatorHelper.assertNotNull&#40;"params", getParams&#40;&#41;&#41;;
        &#125;
        
        public Properties getUriParams&#40;HttpServletRequest request&#41; throws OnthooApplicationException &#123;
            String uri = request.getRequestURI&#40;&#41;;
            Matcher matcher = getPattern&#40;&#41;.matcher&#40;uri&#41;;
            if &#40;matcher.find&#40;&#41;&#41; &#123;
                
                if &#40;matcher.groupCount&#40;&#41; != getParams&#40;&#41;.length&#41; &#123;
                    throw new OnthooApplicationException&#40;"The pattern '" + getPattern&#40;&#41;.pattern&#40;&#41; + " has " + matcher.groupCount&#40;&#41;
                            + " groups instead of the expected " + getParams&#40;&#41;.length&#41;;
                &#125;
                
                Properties uriParams = new Properties&#40;&#41;;
                for &#40;int i = 0; i < matcher.groupCount&#40;&#41;; i++&#41; &#123;
                    String value = matcher.group&#40;i+1&#41;;
                    uriParams.setProperty&#40;getParams&#40;&#41;&#91;i&#93;,value&#41;;
                    if &#40;log.isDebugEnabled&#40;&#41;&#41; &#123;
                        log.debug&#40;"Param " + i + " for uri '" + uri + "'&#58; '" + getParams&#40;&#41;&#91;i&#93; + "' = '" + value + "'"&#41;;
                    &#125;
                &#125;
                
                return uriParams;
                            
            &#125; else &#123;
                throw new OnthooApplicationException&#40;"The pattern '" + getPattern&#40;&#41;.pattern&#40;&#41; + " does not match '" + uri + "'"&#41;;
            &#125;
        &#125;
        
        public void setPatternString&#40;String pattern&#41; &#123;
            this.pattern = Pattern.compile&#40;pattern&#41;;
        &#125;
    
        /**
         * @return Returns the pattern.
         */
        public Pattern getPattern&#40;&#41; &#123;
            return this.pattern;
        &#125;
        /**
         * @param pattern The pattern to set.
         */
        public void setPattern&#40;Pattern pattern&#41; &#123;
            this.pattern = pattern;
        &#125;
        /**
         * @return Returns the params.
         */
        public String&#91;&#93; getParams&#40;&#41; &#123;
            return this.params;
        &#125;
        /**
         * @param params The params to set.
         */
        public void setParams&#40;String&#91;&#93; params&#41; &#123;
            this.params = params;
        &#125;
    
    &#125;

    Comment


    • #3
      Pieter,

      Thanks for the reply, but unfortunatelly that sollution is the one I'm trying to avoid, e.g. to avoid double configuration.

      For example, if my controller is defined as

      <bean name="myCtrl" class="..."></bean>

      And I have a mapping "/myCtrl/something/**" I can put that string into myCtrl's property but then I would have "/myCtrl/something" appear twice in my config file, which is rather cumbersome. I'd like to have myCtrl (or to be more precise, controller class configured as myCtrl) _TO KNOW_ that it was called as /myCtrl/something/and/here/are/the/params.

      If that is not possible, then I'll have to use your suggestion, but somehow I thing the sollution is somewhere near but I just cannot see it.

      Thanks again and kind regards,
      Davor

      Comment


      • #4
        Back to this issue...

        I found a solution which looks a little bit kludgy but at least works (at least for me :-)

        First, the controller must implement BeanNameAware and ApplicationContextAware, then add the following method to it:

        Code:
            private void addControllerMappingToRequest&#40;HttpServletRequest request&#41; throws Exception &#123;
                MySimpleUrlHandlerMapping handlerMapping = &#40;MySimpleUrlHandlerMapping&#41; applicationContext.getBean&#40;"urlMapping"&#41;;
                String mapping = handlerMapping.getMappingForBean&#40;beanName&#41;;
                log.debug&#40;"Mapping for " + beanName + " is " + mapping&#41;;
                request.setAttribute&#40;REQUEST_PATH_MAPPING, mapping&#41;;
            &#125;
        MySimpleUrlHandlerMapping extends Spring's SimpleUrlHandlerMapping to expose path to controller hashmap, therefore you have to use it in your *-servlet.xml context xml file:

        Code:
        	<bean id="urlMapping" class="MySimpleUrlHandlerMapping">
                <property name="mappings">
                    <props>
        				<prop key="some/mapping/**">someController</prop>
        				<prop key="another/mapping/**">anotherController</prop>
        MySimpleUrlHandlerMapping wraps some setter methods and adds a new one, getMappingForBean(beanName):

        Code:
        public class ImsSimpleUrlHandlerMapping extends SimpleUrlHandlerMapping &#123;
        
            private final Map localUrlMap = new HashMap&#40;&#41;;
        
            public void setMappings&#40;Properties mappings&#41; &#123;
                localUrlMap.putAll&#40;mappings&#41;;
                super.setUrlMap&#40;localUrlMap&#41;;
            &#125;
        
            public void setUrlMap&#40;Map urlMap&#41; &#123;
                localUrlMap.putAll&#40;urlMap&#41;;
                super.setUrlMap&#40;localUrlMap&#41;;
            &#125;
            
            public String getMappingForBean&#40;String beanName&#41; &#123;
                Iterator iter = localUrlMap.keySet&#40;&#41;.iterator&#40;&#41;;
                while &#40;iter.hasNext&#40;&#41;&#41; &#123;
                    String key = &#40;String&#41; iter.next&#40;&#41;;
                    if &#40;localUrlMap.get&#40;key&#41;.equals&#40;beanName&#41;&#41; &#123;
                        return key;
                    &#125;
                &#125;
                
                return null;
            &#125;
        &#125;
        After finding which path was used to trigger the controller, put the path into request (see above) and use it later when you want to recognize what part of the request path is actually the parameter. E.g. if I need to load an image from url http://host/app/action/my/mapping/some/dir/image.jpg, and "my/mapping" is the mapping and "some/dir/image.jpg" is the parameter I would have something like this:

        Code:
        	    String mapping = &#40;String&#41; request.getAttribute&#40;MyController.REQUEST_PATH_MAPPING&#41;;
        		mapping = mapping.substring&#40;0, mapping.lastIndexOf&#40;'/'&#41;&#41;;
        		
        		log.info&#40;"Mapping&#58; " + mapping&#41;;
        		
        		String pathInfo = request.getPathInfo&#40;&#41;;
        		String imageName = pathInfo.substring&#40;pathInfo.lastIndexOf&#40;mapping&#41; + mapping.length&#40;&#41;&#41;;
        
        		log.info&#40;"Image name&#58; " + imageName&#41;;
        Again, this solution works for me but it has some drawbacks (what if there are more than one url mapping for a certain controller bean?).

        If you have a better solution, or if you see some major problems with this one, please let me know.

        Comment


        • #5
          Little variation - retrieving mapping by controller referenc

          I retrieve the mapping using the object instead of its name.

          E.g. http://localhost/main/files/attachme...Attachment.txt

          main = application context
          files = mapping pattern
          pathinfo = /attachments/dir1/myAttachment.txt
          computed mapping = /attachments
          resulting path = /dir1/myAttachment.txt

          web.xml fragment
          Code:
            <servlet-mapping>
              <servlet-name>main</servlet-name>
              <url-pattern>/files/*</url-pattern>
            </servlet-mapping>
          x-servlet.xml fragment
          Code:
            <bean id="secure_adminBrowseAttachments"
                     class="admin.AdminFileServlet">
              <property name="rootDirPath">
                <ref bean="partAttachmentPaths"/>
              </property>
            </bean>
          &#91;...&#93;
            <bean id="secureHandlerMapping" class="MySimpleUrlHandlerMapping">
              &#91;...&#93;
              <property name="urlMap">
                <map>
                  <entry key="/attachments/**"><ref local="secure_BrowseAttachments"/></entry>
                </map>
              </property>
            </bean>
          MySimpleUrlHandlerMapping fragment
          Code:
            public String getMappingForBean&#40;Object controller&#41; &#123;
              Iterator iter = localUrlMap.keySet&#40;&#41;.iterator&#40;&#41;;
              while &#40;iter.hasNext&#40;&#41;&#41; &#123;
                String key = &#40;String&#41; iter.next&#40;&#41;;
                if &#40;localUrlMap.get&#40;key&#41; == controller&#41; &#123;
                  return key;
                &#125;
              &#125;
          
              return null;
            &#125;

          Comment


          • #6
            Regex is the answer

            Matching plus reporting details of the match are well performed by java.util.Regex using match groups.

            I've created a RegexHandlerMapping that matches urls by regex and reports the corresponding match groups. See http://opensource.atlassian.com/proj...browse/SPR-703

            Matthias

            Comment


            • #7
              You may also have a look at the UrlRewriter filter. It supports complete URL "abstraction". In addition to inbound mapping known from mod_rewrite, it can also rewrite urls outbound (in your JSPs if you used <c:url/>). That's a very cool thing. And it works nice with Spring and Sitemesh.

              http://tuckey.org/urlrewrite/


              -andi

              Comment

              Working...
              X