3 Replies Latest reply on May 3, 2007 3:05 AM by Shane Bryzak

    <param> in pages.xml doesn't accept multi-valued request par

    Ben Kyrlach Newbie

      Back in the day (well, more like several months ago) I developed an admittedly bad Seam app to replace a part of a website originally written in struts/jsp. Due to the somewhat limited nature of struts, I found myself heavily relying on @RequestParameter to allow me to pass values to the Seam part of the application from the traditional JSP portion.

      Fast forward to now, lots of great improvements to Seam. In many ways, it makes my code from before seem so bad it's scary. One problem in particular (handling requests made directly to a view that required the user to be logged in) has been greatly simplified with the addition of Seam security. However, I'm running into a problem.

      I have a page that's a simple JSP that represents some search results. The search results are enclosed in an HTML form to allow mass updates on the search results (in this particular case, the user is allowed to bookmark items from the search results... kinda like adding items to a shopping cart or wish list). So what I've done is have the page submit to a JSF view that has a page action associated with it that will capture the users bookmarks. I'm also using features from Seam security to redirect the user to the login page if they're not logged in, and then return them to this bookmark view so that the page action will continue to save their bookmarks.

      However, this results in a problem as in order to have Seam remember the request parameters from the form submission, I have to have the parameters defined as part of the page using the tag in pages.xml. Unfortunantly for me, the Param class in Seam that handles these parameters doesn't accept multi-valued request parameters (it throws an IllegalArgumentException).

      I'm not allowed to convert this page to Seam or Faces/Facelets. That being the case, I've decided to take a look at the Param class from Seam and see how difficult it would be to change it to allow for multi-valued request parameters. I've since made some slight modifications that seem to work, but I'd like the community to review it and give me some suggestions for improvement. The code I've changed is as follows...

      from org.jboss.seam.pages.Param

       /**
       * Get the current value of a page parameter from the request parameters
       */
       public Object getValueFromRequest(FacesContext facesContext, Map<String, String[]> requestParameters)
       {
       String[] parameterValues = requestParameters.get( getName() );
       if (parameterValues==null || parameterValues.length==0)
       {
       return null;
       }
       Converter converter = null;
       try
       {
       converter = getConverter();
       }
       catch (RuntimeException re)
       {
       // YUCK! due to bad JSF/MyFaces error handling
       return null;
       }
      
       Class<?> methodType = valueBinding.getType();
       if (methodType.isAssignableFrom(Collection.class))
       {
       List retVal = new ArrayList();
       if(converter != null)
       {
       for(String parameterValue: parameterValues)
       {
       Object convertedValue = converter.getAsObject(facesContext, facesContext.getViewRoot(), parameterValue);
       retVal.add(convertedValue);
       }
       }
       else
       {
       throw new RuntimeException("You must specify a converter for bean properties that are a collection.");
       }
      
       try
       {
       return methodType.cast(retVal);
       }
       catch (Exception e)
       {
       throw new RuntimeException(e);
       }
       }
      
       if(parameterValues.length > 1 && !(methodType.isAssignableFrom(Collection.class)))
       {
       throw new RuntimeException("Request parameter was multi-valued and bean property doesn't support collection interface.");
       }
      
       String stringValue = parameterValues[0];
       return converter==null ?
       stringValue :
       converter.getAsObject( facesContext, facesContext.getViewRoot(), stringValue );
       }
      


      Basically, I've changed the method to see if the value binding associated with this parameter is expecting a list or other form of collection. There doesn't seem to be a way to find the generic type of a particular collection (i.e if you have a List there doesn't seem to be a way to find out that this list only accepts String values), so I'm forcing the specification of a converter for value bindings that are expecting a collection. I then iterate through the array of request parameter values and convert each one and place it into a collection. Then I convert the collection to the type expected by the value binding.

      This method certainly has it's flaws, which is why I'm posting it out here. I'm hoping I can refine this code into something worthy of a patch.