Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.



 

...

Span
stylefont-size:2em;font-weight:bold
JAX-RS: Understanding the Basics

...



Table of Contents

What is New in JAX-RS 2.1

...

Default CompletionStageRxInvoker can be accessed via Invocation.rx().

RxJava

...

Custom RxInvokerProvider can be registered with the Client as a provider. CXF ships one three such providercustom providers, org.apache.cxf.jaxrs.rx.client.ObservableRxInvokerProvider (RxJava1),

org.apache.cxf.jaxrs.rx2.client.ObservableRxInvokerProvider (RxJava2) and org.apache.cxf.jaxrs.rx2.client.FlowableRxInvokerProvider (RxJava2).

Registering it with the Client allows for working with RxJava Observable RxJava1 Observable or RxJava2 Observable or Flowable by doing Invocation.rx(Class<T> clazz), more specificallyexample,

Invocation.rx(org.apache.cxf.jaxrs.rxrx2.client.ObservableRxInvokerFlowableRxInvoker.class), etc.

Please see JAX-RS RxJava for more information.

RxJava2

RxJava2 can be used as a rx type when registered.  Please see JAX-RS RxJava for full information.

Project Reactor

Project Reactor can be used as a rx type when registered.  Please see JAX-RS Project Reactor Support for full information.

CompletableFuture as a method return value

...

org.apache.cxf/cxf-rt-rs-sse/3.2.0 dependency will need to be added. CXF SSE implementation current currently depends on Atmosphere.

SubResources as Classes

...

CXF 3.0.4 introduces a new extension, a org.apache.cxf.jaxrs.ext.DefaultMethod annotation. It can be used to match arbitrary HTTP methods on a single resource method. This can be used in some advanced scenarious for integrating the CXF JAX-RS runtime into non-JAX-RS environments as well as in cases where it is awkward/difficult to have every HTTP method listed for a given path segment. CXF users need to be aware using this option will lead to a non-portable JAX-RS code.

 


Return types

Either javax.ws.rs.core.Response or custom type can be returned. javax.ws.rs.core.Response can be used to set the HTTP response code, headers and entity. JAX-RS MessageBodyWriters (see below) are in charge of serializing the response entities, those which are returned directly or as part of javax.ws.rs.core.Response.

...

CXF StreamingResponse

CXF 3.0.0 introduces StreamingResponse extension. It can be used with the WebSocket transport or as a possible replacement for the code working with StreamingOutput.

For For example, consider that a number of resources need to be returned as they become available:

...

Code Block
languagexml
<bean id="exceptionMapper" class="org.apache.cxf.jaxrs.impl.WebApplicationExceptionMapper">
   <property name="addMessageToResponse" value="true" />
</bean>


Note that the custom WebApplicationException mapperWebApplicationExceptionMapper, if registered, will be preferred to the default one in normal cases. However, the default WebApplicationExceptionMapper will win over custom WebApplicationExceptionMapper which are less specific (ex, RuntimeException mappers) but which expect to catch WebApplicationException.  And there is a property "default.wae.mapper.least.specific" is introduced to ensure a CXF custom WebApplicationExceptionMapper can still win over in this specific case.

Dealing with Parameters

PathParam annotation is used to map a given Path template variable to a method parameter.
For example :

...

Code Block
java
java
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.MultivaluedMap;

import org.apache.cxf.jaxrs.ext.ResourceComparator;
import org.apache.cxf.jaxrs.model.ClassResourceInfo;
import org.apache.cxf.jaxrs.model.OperationResourceInfo;
import org.apache.cxf.jaxrs.model.OperationResourceInfoComparatorOperationResourceInfoComparatorBase;
import org.apache.cxf.jaxrs.model.Parameter;
import org.apache.cxf.jaxrs.utils.HttpUtils;
import org.apache.cxf.jaxrs.util.JAXRSUtils;
import org.apache.cxf.message.Message;

public class QueryResourceInfoComperatorQueryResourceInfoComparator extends OperationResourceInfoComparatorOperationResourceInfoComparatorBase implements
         ResourceComparator {

     public QueryResourceInfoComperatorQueryResourceInfoComparator() {
         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) {
         // HTTP method
         final String httpMethod = HttpUtils.getProtocolHeader(message, Message.HTTP_REQUEST_METHOD, HttpMethod.POST, true);
        
         // Check if CXF can make a decision
         final int cxfResult = super.compare(oper1, oper2, httpMethod);
         if (cxfResult != 0) {
             return cxfResult;
         }

         int op1Counter = getMatchingRate(oper1, message);
         int op2Counter = getMatchingRate(oper2, message);

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

     /**
      * This method calculates a number indicating a good or bad match between
      * values provided within the request and expected method parameters. A
      * higher number means a better match.
      *
      * @param operation
      *            The operation to be rated, based on contained parameterInfo
      *            values.
      * @param message
      *            A message containing query from user request
      * @return A positive or negative number, indicating a good match between
      *         query and method
      */
     protected int getMatchingRate(OperationResourceInfo operation, Message message) {

         List<Parameter>  params = operation.getParameters();
         if (params == null || params.size() == 0)
             return 0;

         // Get Request QueryParams
         Set<String>  qParams = getParams((String) message.get(Message.QUERY_STRING));

         int rate = 0;
         for (Parameter p : params) {
             switch (p.getType()) {
             case QUERY:
                 if (qParams.contains(p.getName()))
                     rate += 2;
                 else if (p.getDefaultValue() == null)
                     rate -= 1;
                 break;
             // optionally support other parameter types such as headers, etc
             // case HEADER:
             //  break;
             default:
                 break;
             }
         }
         return rate;
     }

     /**
      * @param query
      *            URL Query Example: 'key=value&key2=value2'
      * @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;

         MultivaluedMap<String, String> allQueries =
             JAXRSUtils.getStructuredParams(query, "&", false, false);
         return allQueries.keySet();
     }
} 

...

The core part of the solution is to inject the UriInfo object into method "getCustomers". This helper object allows for extracting useful information about the current URI context, but more importantly allows for getting the UriBuilder object. UriBuilder has multiple appender methods for building the URI for each object; in our case to the stem URI we can append path in multiple ways, providing it as a string (which we actually want to avoid here) or a resource (class or method) to extract the @Path value. Finally UriBuilder must have values bound to its template variables to render the actual URI. This case in action looks like this:

...