Hi Jan

This looks very neat, more comments below
On 27/11/12 15:25, janb wrote:
Hi Sergey,

Thank you for your reply. If just developed a generic sample that uses CXF 
standard class and operation selection algorithms, but extends it with a 
QueryParam selection algorithm. If all other conditions in CXF match and hence 
CXF cannot decide which method to select best, my QueryParam selection 
algorithm comes into place. This algorithm chooses the method with most matches 
between provided parameters in query and matching QueryParam annotations within 
the method. Additional (not matching) QueryParam annotations will decrease the 
matching rate, while method parameters with a default value will be ignored (in 
the rating).

If you think this code is of any value for CXF, please feel free to extend the 
current selection strategy with my QueryParam selection algorithm. I guess the 
best place for my algorithm would be another JAXRSUtils method, like
JAXRSUtils.compareQueryParams(...) to be called at the end of 
OperationResourceInfoComparator.compare(...).


public class QueryResourceInfoComperator extends 
OperationResourceInfoComparator implements
         ResourceComparator {

     public QueryResourceInfoComperator() {
         super(null, null);
     }

     @Override
     public int compare(ClassResourceInfo cri1, ClassResourceInfo cri2, Message 
message) {
         // Leave Class selection to CXF
         return 0;
     }

     @Override
     public int compare(OperationResourceInfo oper1, OperationResourceInfo 
oper2, Message message) {

         // Check if CXF can make a decision
         int cxfResult = super.compare(oper1, oper2);
         if (cxfResult != 0)
             return cxfResult;

         // Compare QueryParam annotations
         Set<String>  qParams = getParams((String) 
message.get(Message.QUERY_STRING));
         int op1Counter = 
getMatchingRate(getAnnotations(oper1.getMethodToInvoke().getParameterAnnotations()),
                 qParams);
         int op2Counter = 
getMatchingRate(getAnnotations(oper2.getMethodToInvoke().getParameterAnnotations()),
                 qParams);

         return op1Counter == op2Counter
                 ? 0
                 : op1Counter<  op2Counter
                         ? 1
                         : -1;
     }

     /**
      * This method calculates a number indicating a good or bad match between
      * queryParams from request and annotated method parameters. A higher 
number
      * means a better match.
      *
      * @param annotations
      *            Map contains name of QueryParam method parameters as a key 
and
      *            a Boolean value indicating an existing default value for this
      *            parameter.
      * @param queryParams
      *            A Set of query parameters provided within the request
      * @return A positive or negative number, indicating a good match between
      *         query and method
      */
     protected int getMatchingRate(Map<String, Boolean>  annotations, 
Set<String>  queryParams) {
         int rate = 0;
         for (String anno : annotations.keySet()) {
             if (queryParams.contains(anno)) {
                 // URL query matches one method parameter
                 rate += 2;
             } else if (!annotations.get(anno).booleanValue()) {
                 // No default value exists for method parameter
                 rate -= 1;
             }
         }
         return rate;
     }

     /**
      * @param opParamAnnos
      *            Array containing all annotations for all method parameters
      * @return Key in Map is QueryParam name, and Value indicates if a default
      *         value is present.
      */
     protected Map<String, Boolean>  getAnnotations(Annotation[][] 
opParamAnnos) {
         Map<String, Boolean>  parameterAnnos = new HashMap<String, Boolean>();

         if (opParamAnnos.length == 0)
             return parameterAnnos;

         for (Annotation[] pAnnos : opParamAnnos) {
             QueryParam qParam = null;
             DefaultValue dValue = null;
             for (Annotation anno : pAnnos) {
                 if (anno instanceof QueryParam)
                     qParam = (QueryParam) anno;
                 if (anno instanceof DefaultValue)
                     dValue = (DefaultValue) anno;
             }
             parameterAnnos.put(qParam.value(), Boolean.valueOf((dValue != 
null)));
         }

         return parameterAnnos;
     }

     /**
      * @param query
      *            URL Query
      * @return A Set of all keys, contained within query.
      */
     protected Set<String>  getParams(String query) {
         Set<String>  params = new HashSet<String>();
         if (query == null || query.length() == 0)
             return params;

         try {
             for (String param : query.split("&")) {
                 String pair[] = param.split("=");
                 String key = URLDecoder.decode(pair[0], "UTF-8");
                 params.add(key);
             }
         } catch (UnsupportedEncodingException e) {
             e.printStackTrace();
         }
         return params;
     }
}


I think it is difficult to generalize given that another user may want a similar support for the selection based on matrix/header/form parameters, and the other complexity is that we can have repeating parameters, example, "a=1&a=2" query, etc.

However it definitely makes sense to update the docs and show what does it mean to customize the selection algo, so what I will do is I will update the page and paste the code - lets see may be we can push some of the code to CXF eventually

thanks, Sergey

Best regards.
Jan

From: Sergey Beryozkin-5 [via CXF] 
[mailto:[email protected]]
Sent: Dienstag, 27. November 2012 11:31
To: Jan Bernhardt
Subject: Re: REST Method selection for different QueryParam's

Hi Jan
On 27/11/12 10:12, janb wrote:

Hi @all,

I'm wondering about the selection strategy in CXF for different Query
Parameter. The documentation [1] does not cover this at all.

A simple system-test provided the impression to me, that CXF has no valid
selection strategy in place for handling different query parameters. Is this
assumption correct? Should I create a Jira ticket for a better support?

Here is my sample code. No matter which parameter have been provided within
my URL, only and always the first method was selected by CXF.

@Path("/paramTest")
public class MySimpleService {

      @GET
      public String getFoo(@QueryParam("foo") String foo){
          return "foo:" + foo;
      }

      @GET
      public String getFooBar(@QueryParam("foo") String foo,
@QueryParam("bar") String bar){
          return "foo:" + foo + " bar:" + bar;
      }

      @GET
      public String getTest(@QueryParam("test") String test){
          return "test:" + test;
      }
}

[1]
http://cxf.apache.org/docs/jax-rs-basics.html#JAX-RSBasics-Overviewoftheselectionalgorithm.


Only URI path segment, HTTP method and in/out media types are taken into
consideration when selecting the candidates. If you prefer to have
individual methods having the same HTTP Method/Uri Path/Media Types but
with specific query parameters then the only way to get it managed is to
use a CXF ResourceComparator where, in this case, you can affect the
ordering of specific resource methods by checking the current
Message.QUERY_STRING available on the CXF Message

A simpler alternative is to have a single method and work with UriInfo, say

@Context
private UriInfo ui;

@GET
public String getFooOrBar() {
      MultivaluedMap<String, String>  params = ui.getQueryParameters();
      String foo = params.getFirst("foo");
      String bar = params.getFirst("foo");
      // etc
}

HTH, Sergey



--
View this message in context: 
http://cxf.547215.n5.nabble.com/REST-Method-selection-for-different-QueryParam-s-tp5719187.html
Sent from the cxf-user mailing list archive at Nabble.com.


--
Sergey Beryozkin

Talend Community Coders
http://coders.talend.com/

Blog: http://sberyozkin.blogspot.com

________________________________
If you reply to this email, your message will be added to the discussion below:
http://cxf.547215.n5.nabble.com/REST-Method-selection-for-different-QueryParam-s-tp5719187p5719190.html
To unsubscribe from REST Method selection for different QueryParam's, click 
here<http://cxf.547215.n5.nabble.com/template/NamlServlet.jtp?macro=unsubscribe_by_code&node=5719187&code=amJlcm5oYXJkdEB0YWxlbmQuY29tfDU3MTkxODd8LTEzMDQ4ODk1MjM=>.
NAML<http://cxf.547215.n5.nabble.com/template/NamlServlet.jtp?macro=macro_viewer&id=instant_html%21nabble%3Aemail.naml&base=nabble.naml.namespaces.BasicNamespace-nabble.view.web.template.NabbleNamespace-nabble.view.web.template.NodeNamespace&breadcrumbs=notify_subscribers%21nabble%3Aemail.naml-instant_emails%21nabble%3Aemail.naml-send_instant_email%21nabble%3Aemail.naml>




--
View this message in context: 
http://cxf.547215.n5.nabble.com/REST-Method-selection-for-different-QueryParam-s-tp5719187p5719219.html
Sent from the cxf-user mailing list archive at Nabble.com.


--
Sergey Beryozkin

Talend Community Coders
http://coders.talend.com/

Blog: http://sberyozkin.blogspot.com

Reply via email to