Versions Compared

Key

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



{span:style=
Span
Wiki Markup
style
font-size:2em;font-weight:bold
}JAX-RS : Advanced Features



Table of Contents

JMS Support

CXF has been designed such that multiple transports can be supported for a given endpoint. CXF JAX-RS endpoint and proxies can optionally support the JMS transport.

Endpoints

If you would like your JAXRS endpoint be capable of serving not only HTTP but also JMS requests then you need to specify a JMS transportId, example:

Code Block
xml
xml
 Features{span}

{toc}

h1. JMS Support

CXF has been designed such that multiple transports can be supported for a given endpoint. If you would like your JAXRS endpoint be capable of serving not only HTTP but also JMS requests then you need to specify a JMS transportId, example :

{code:xml}
<jaxrs:server serviceName="s:BookService" transportId="http://cxf.apache.org/transports/jms" address="/">
 <jaxrs:serviceBeans>
   <bean class="org.apache.cxf.systest.jaxrs.JMSBookStore"/>
 </jaxrs:serviceBeans>
</jaxrs:server>
{code} 

Additionally,

...

JMS

...

queue

...

or

...

topic

...

configuration

...

needs

...

to

...

be

...

done,

...

for

...

example,

...

please

...

see

...

this

...

beans.xml

...

. Please note how a serviceName attribute is used to specify a service QName for a jaxrs endpoint (default is {http://reverse.package.name}ServiceClassName),

...

this

...

service

...

name

...

is

...

used

...

to

...

configure

...

a

...

jms

...

destination.

...

Here

...

is

...

the

...

actual

...

test

...

.

Here are JMS properties which can help with matching a required method on the JAXRS endpoint :

  • "Content-Type"

...

  • :

...

  • default

...

  • is

...

  • "text/xml"

...

  • "Accept"

...

  • :

...

  • default

...

  • is

...

  • "

...

  • /

...

  • "

...

  • "OnewayMessage"

...

  • :

...

  • default

...

  • is

...

  • "false"

...

  • "org.apache.cxf.message.Message.REQUEST_URI"

...

  • :

...

  • default

...

  • is

...

  • "/"

...

  • "org.apache.cxf.message.Message.HTTP_REQUEST_METHOD"

...

  • :

...

  • default

...

  • is

...

  • "POST"

...

If

...

JMS

...

messages

...

are

...

sent

...

to

...

topic

...

destinations

...

then

...

one

...

has

...

to

...

either

...

set

...

a

...

"OnewayMessage"

...

property

...

or

...

ensure

...

that

...

target

...

JAXRS

...

methods

...

are

...

annotated

...

with

...

org.apache.cxf.jaxrs.ext.Oneway.

...

As

...

far

...

as

...

REQUEST_URI

...

is

...

concerned,

...

it

...

is

...

initially

...

matched

...

against

...

a

...

jaxrs:server/@address.

...

So

...

if

...

REQUEST_URI

...

is

...

not

...

set

...

or

...

set

...

to

...

"/"

...

then

...

jaxrs:server/@address

...

has

...

to

...

be

...

set

...

to

...

"/".

...

If

...

REQUEST_URI

...

is

...

set

...

to

...

"/bar/foo"

...

and

...


jaxrs:server/@address

...

is

...

set

...

to

...

"/bar"

...

then

...

it

...

will

...

be

...

'/foo'

...

which

...

will

...

be

...

used

...

to

...

find

...

a

...

root

...

resource

...

class

...

and

...

its

...

method.

...

By

...

referencing

...

a

...

bean

...

such

...

as

...

'org.apache.cxf.systest.jaxrs.JMSBookStore'

...

from

...

multiple

...

jaxrs

...

endpoints

...

you

...

can

...

ensure

...

that

...

both

...

HTTP

...

and

...

JMS

...

requests

...

are

...

handled

...

by

...

the

...

same

...

service

...

bean.

...

In

...

such

...

cases

...

you

...

may

...

want

...

to

...

use

...

a

...

CXF

...

JAXRS

...

specific

...

ProtocolHeaders

...

context

...

which

...

will

...

let

...

you

...

get

...

either

...

HTTP

...

or

...

JMS headers.

Client

Starting from CXF 2.5.5 and CXF 2.6.2 it is possible to use the client proxies to invoke on JMS endpoints. All one needs to do is to provide a JMS endpoint address and then continue working with the proxy as usual. For example:

Code Block
java
java
// setup the the client
String endpointAddressUrlEncoded = "jms:jndi:dynamicQueues/test.jmstransport.text"
             + "?jndiInitialContextFactory=org.apache.activemq.jndi.ActiveMQInitialContextFactory"
             + "&replyToName=dynamicQueues/test.jmstransport.response"
             + "&jndiURL=tcp://localhost:" + JMS_PORT
             + "&jndiConnectionFactoryName=ConnectionFactory";
               
JMSBookStore client = JAXRSClientFactory.create(endpointAddressUrlEncoded, JMSBookStore.class);
Book book = client.getBook("123");
assertEquals("Get a wrong response code.", 200, WebClient.client(client).getResponse().getStatus());
assertEquals("Get a wrong book id.", 123, book.getId());

The client runtime will set up the JMS properties described in the previous section according to JAX-RS and other annotations (such as org.apache.cxf.jaxrs.ext.Oneway) available in JMSBookStore resource class.

Advanced Search

Please see JAX-RS Search for more information

Oneway invocations

Resource methods with an org.apache.cxf.jaxrs.ext.Oneway annotation will be invoked oneway with the original request returning 202 HTTP status. HTTP or JMS clients can also add a "OnewayRequest" header if adding Oneway annotations is not an option.

Support for Continuations

Please see this blog entry describing how JAX-RS (and indeed) JAX-WS services can rely on the CXF Continuations API.

Please see the Continuations page for more information.

Client-side caching

If the JAX-RS Response message contains a "Cache-Control" HTTP header, then it is possible to cache the Response payload on the client side using the CacheControlFeature. CacheControlFeature uses the javax.cache.Caching API (which is optional in cxf-rt-rs-client). Therefore, to use the CacheControlFeature it is necessary to add the javax.cache API as well as an implementation (such as EhCache). For example:

Code Block
xml
xml
<dependency>
    <groupId>javax.cache</groupId>
    <artifactId>cache-api</artifactId>
    <version>1.0.0</version>
</dependency>
<dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>3.0.3</version>
</dependency>

CacheControlFeature parses the Cache-Control header and caches the Response payload if appropriate for the "max-age" attribute of Cache-Control (or the "Expires" HTTP header if max-age is not specified). The next time the client calls out to the remote service (only GET is supported for now), the Response payload is retrieved from the cache and returned instead (assuming it is not expired), thus avoiding an unnecessary round-trip. Here is an example:

Code Block
xml
xml
CacheControlFeature cacheControlFeature = new CacheControlFeature();
cacheControlFeature.setCacheResponseInputStream(true);
Client client = ClientBuilder.newBuilder()
                           .register(cacheControlFeature)
                           .build();
WebTarget target = client.target(endpointAddress);

// First call
Response response = target.request().get();
// Second call should be cached
target.request().get();

If the initial response from the service contains an "ETag" HTTP header, then once the message has expired, CXF will send this value to the service as the "If-None-Match" header. Similarly, "Last-Modified" is sent as "If-Modified-Since". The CXF client will also cache the expired payload. If the service responds with a 304 status code, then the old message payload is returned to the client.

Server-side caching

Ehcache-Web and other similar frameworks can be used to provide an advanced support for the server-side caching.

For example, the only thing you need to do to interpose Ehcache-Web on top of CXF JAX-RS endpoints is to add the following declarations to the web.xml, assuming the name of the war is 'ehcache-cxf':

Code Block
xml
xml
<context-param>
    <param-name>webAppRootKey</param-name>
    <param-value>ehcache-cxf</param-value>
</context-param>
<filter>
    <filter-name>SimplePageCachingFilter</filter-name>
    <filter-class>net.sf.ehcache.constructs.web.filter.SimplePageCachingFilter</filter-class>
    <init-param>
        <param-name>varyHeader</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
    
<filter-mapping>
    <filter-name>SimplePageCachingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Please see the Ehcache-Web page for more information on how to configure it, here is one example:

Code Block
xml
xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="../../main/config/ehcache.xsd"
    updateCheck="false"
    monitoring="autodetect"
    dynamicConfig="true">
	
	<defaultCache
		maxElementsInMemory="10"
		eternal="false"
		timeToIdleSeconds="5"
		timeToLiveSeconds="10"
		overflowToDisk="true" />
	
    <cache name="SimplePageCachingFilter"
		maxElementsInMemory="100"
		eternal="false"
		overflowToDisk="false"
		timeToIdleSeconds="5"
		timeToLiveSeconds="10"
        memoryStoreEvictionPolicy="LFU" />
</ehcache>

This configuration has to be saved in ehcache-web.xml file and available as a class-path resource starting from the root.

RESTful services without annotations

One of the latest CXF JAX-RS extensions allows users to provide external models with the information which the runtime typically gets from JAX-RS annotations like @Path, @PathParam, @Consumes, @Produces, etc.
There might be a number of cases when it can be advantageous to describe how a given resource can be exposed as a RESTful service without actually modifying this resource. For example, when new dynamic interface implementations are registered, when no source code can be modified, when the cost of future updates (for ex, modifying the value of @Path annotations) is considered to be expensive, etc.

User model schema type is described in the jaxrs.xsd.

The top-level 'model' element can have 'resource' children elements. A 'resource' element describes a resource class which can be either a root resource class or a sub-resource one and it can have attributes describing 'path', 'produces' and 'consumes' values and it has a 'name' attribute which identifies a fully-qualified resource class.
A 'resource' element can have a number of 'operation' elements pointing to resource methods (with its 'name' attribute) and can have 'path', 'produces', 'consumes' and 'verb' (HTTP method) values. An 'operation' element which has no 'verb' attribute is treated as a sub-resource locator - a corresponding resource class has to be available in the model with its 'name' attribute matching the return type's name of this operation.
Every operation can have a number of 'param' elements. A 'param' element should have its 'name' attribute matching a corresponding parameter name in the class resource method. Its 'type' can have the following values : 'PATH', 'QUERY', 'CONTEXT', 'HEADER', 'MATRIX', 'COOKIE', 'FORM' or 'REQUEST_BODY'. Parameters corresponding to response types do not have to be described. It can also have 'defaultValue' and 'encoded' values being set.

Starting from CXF 2.3.2-SNAPSHOT a "oneway" attribute can also be applied to individual operations.

Here is an example :

Code Block
xml
xml
 headers. 

h1. FIQL search queries

CXF JAXRS (since 2.3.0) lets users do the advanced search queries based on the [Feed Item Query Language|http://tools.ietf.org/html/draft-nottingham-atompub-fiql-00](FIQL). FIQL lets users express complex search expressions using an intuitive and URI friendly language.

For example, a query such as

{code:java}
?_search=name==CXF;version=ge=2.2
{code}

lets users to search for all the Apache projects with the name 'CXF' and the version greater or equals to '2.2'. The initial '=' separates the name of the query '_search' from the FIQL expression, while '==' and '=ge=' convey 'equals to' and 'greater or equals to' respectively.
An expression such as "name==CXF*" can be used to the partial equality checks (in this example: the name should start from "CXF").

More complex composite expressions can also be expressed easily enough.

Note that either '_search' or '_s' query has to be used to pass the FIQL expression.

h2. Consuming FIQL queries

To work with FIQL queries, a [SearchContext|http://svn.apache.org/repos/asf/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/SearchContext.java] needs be injected into an application code and used to retrieve a [SearchCondition|http://svn.apache.org/repos/asf/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/SearchCondition.java] representing the current FIQL query. This SearchCondition can be used in a number of ways for finding the matching data.

For example :

{code:java}
@Path("books")
public class Books {

private Map<Long, Book> books;
@Context
private SearchContext context;

 @GET
 public List<Book> getBook() {

   SearchCondition<Book> sc = searchContext.getCondition(Book.class);
   // SearchCondition#isMet method can also be used to build a list of matching beans

   // iterate over all the values in the books map and return a collection of matching beans
   List<Book> found = sc.findAll(books.values());
   return found;
 }
}
{code}

SearchCondition can also be used to get to all the search requirements (originally expressed in FIQL) and do some manual
comparison against the local data. For example, SearchCondition provides a utility toSQL(String tableName, String... columnNames) method which internally introspects all the search expressions constituting a current query and converts them into an SQL expression:

{code:java}
// find all conditions with names starting from 'ami' 
// and levels greater than 10 :
// ?_s="name==ami*;level=gt=10"
SearchCondition<Book> sc = searchContext.getCondition(Book.class);
assertEquals("SELECT * FROM table 
              WHERE 
              name LIKE 'ami%' 
              AND 
              level > '10'",
              sq.toSQL("table"));
{code}

The SearchCondition.toSQL() method has become deprecated in CXF 2.3.3 and 2.4.0. Using an org.apache.cxf.jaxrs.ext.search.sql.SQLPrinterVisitor is recommended as it will allow for building more advanced SQL expressions. For example: 

{code:java}
// ?_s="name==ami*;level=gt=10"
SearchCondition<Book> sc = searchContext.getCondition(Book.class);
SQLPrinterVisitor<Book> visitor = new SQLPrinterVisitor<Book>("table");
sc.visit(visitor);
assertEquals("SELECT * FROM table 
              WHERE 
              name LIKE 'ami%' 
              AND 
              level > '10'",
              visitor.getResult());
{code}

Note that SQLPrinterVisitor can also be initialized with the names of columns and the field aliases map:

{code:java}
// ?_s="level=gt=10"
SearchCondition<Book> sc = searchContext.getCondition(Book.class);

Map\<, String\> fieldMap = new HashMap\<String, String\>();
fieldMap.put("level", "LEVEL_FIELD");

SQLPrinterVisitor<Book> visitor = new SQLPrinterVisitor<Book>(fieldMap, "table", "LEVEL_COLUMN");
sc.visit(visitor);
assertEquals("SELECT LEVEL_COLUMN FROM table 
              WHERE LEVEL_COLUMN > '10'",
              visitor.getResult());
{code}

The fields map can help hide the names of the actual table columns/record fields from the Web frontend. Example, the users will know that the 'level' property is available while internally it will be converted to a LEVEL_COLUMN name.

Custom visitors producing more optimized SQL or non-SQL expressions can also be introduced.

Here is a possible code template to follow when a custom visitor needs to be written:

{code:java}
public class CustomVisitor<T> impements SearchConditionVisitor<T> {

private StringBuilder sb = new StringBuilder();

public void visit(SearchCondition<T> sc) {
        
        if (sb == null) {
            sb = new StringBuilder();
            // start the expression as needed
        }
        
        PrimitiveStatement statement = sc.getStatement();
        if (statement != null) {
                // ex a > b
                // use statement.getValue()
                // use statement.getConditionType()
                // use statement.getProperty();
                
        } else {
            for (SearchCondition<T> condition : sc.getSearchConditions()) {
                // pre-process
                condition.accept(this);
                // post-process
            }
        }
    }

    public String getResult() {
        // convert the internal state into String
    }
}
{code}

If needed you can access a FIQL query directly and delegate it further to your own custom FIQL handler:

{code:java}

@Path("/search")
public class SearchEngine {
    @Context
    private UriInfo ui;

    @GET
    public List<Book> findBooks() {
        MultivaluedMap<String, String> params = ui.getQueryParameters();
        String fiqlQuery = params.getFirst("_s");
        // delegate to your own custom handler 
}

{code}

CXF 2.4.0 introduces [SearchConditionBuilder|http://svn.apache.org/repos/asf/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/client/SearchConditionBuilder.java] which makes it simpler to build FIQL queries. SearchConditionBuilder is an abstract class that returns a FIQL builder by default:

{code:java}
SearchConditionBuilder b = SearchConditionBuilder.instance();
String fiqlQuery = b.is("id").greaterThan(123).query();

WebClient wc = WebClient.create("http://books.com/search");
wc.query("_s", fiqlQuery);
// find all the books with id greater than 123 
Collection books = wc.getCollection(Book.class);
{code}

Here is an example of building more complex queries:

{code:java}
// OR condition
String ret = b.is("foo").greaterThan(20).or().is("foo").lessThan(10).query();
assertEquals("foo=gt=20,foo=lt=10", ret);

// AND condition
String ret = b.is("foo").greaterThan(20).and().is("bar").equalTo("plonk").query();
assertEquals("foo=gt=20;bar==plonk", ret);

// Complex condition
String ret = b.is("foo").equalTo(123.4).or().and(
            b.is("bar").equalTo("asadf*"), 
            b.is("baz").lessThan(20)).query();
assertEquals("foo==123.4,(bar==asadf*;baz=lt=20.0)", ret);
{code}

h2. Using dates in queries

By default, the date values have to have the following [format|http://download.oracle.com/javase/6/docs/api/java/text/SimpleDateFormat.html]: "yyyy-MM-dd'T'HH:mm:ss.SSSZ", for example:

{code:java}
?_search=time=le=2010-03-11T18:00:00.000+00:00
{code} 

A simpler date format can be supported. Use "search.date-format" and "search.timezone.support" contextual properies, ex, "search.date-format"="yyyy-MM-dd'T'HH:mm:ss" and "search.timezone.support"="false" will let users avoid specifying milliseconds and timezones:

{code:java}
?_search=time=le=2010-03-11T18:00:00
{code}

At the moment, for custom date formats be recognized by SearchConditionBuilder, FIQLSearchConditionBuilder has to be created explicitly:

{code:java}
Map<String, String> props = new HashMap<String, String>();
props.put("search.date-format", "yyyy-MM-dd'T'HH:mm:ss");
props.put("search.timezone.support", "false");

Date d = df.parse("2011-03-01 12:34:00");
        
FiqlSearchConditionBuilder bCustom = new FiqlSearchConditionBuilder(props);
        
String ret = bCustom.is("foo").equalTo(d).query();
assertEquals("foo==2011-03-01T12:34:00", ret);
{code}

h1. Oneway invocations

Resource methods with an org.apache.cxf.jaxrs.ext.Oneway annotation will be invoked oneway with the original request returning 202 HTTP status. HTTP or JMS clients can also add a "OnewayRequest" header if adding Oneway annotations is not an option.

h1. Support for Continuations 

Please see [this blog entry|http://sberyozkin.blogspot.com/2008/12/continuations-in-cxf.html] describing how JAXRS (and indeed) JAXWS services can rely on the CXF Continuations API. 

Please see the [CXF Continuations] page for more information.


h1. RESTful services without annotations 

One of the latest CXF JAX-RS extensions allows users to provide external models with the information which the runtime typically gets from JAX-RS annotations like @Path, @PathParam, @Consumes, @Produces, etc.
There might be a number of cases when it can be advantageous to describe how a given resource can be exposed as a RESTful service without actually modifying this resource. For example, when new dynamic interface implementations are registered, when no source code can be modified, when the cost of future updates (for ex, modifying the value of @Path annotations) is considered to be expensive, etc.

User model schema type is described in the [jaxrs.xsd|http://svn.apache.org/repos/asf/cxf/trunk/rt/frontend/jaxrs/src/main/resources/schemas/jaxrs.xsd]. 

The top-level 'model' element can have 'resource' children elements. A 'resource' element describes a resource class which can be either a root resource class or a sub-resource one and it can have attributes describing 'path', 'produces' and 'consumes' values and it has a 'name' attribute which identifies a fully-qualified resource class. 
A 'resource' element can have a number of 'operation' elements pointing to resource methods (with its 'name' attribute) and can have 'path', 'produces', 'consumes' and 'verb' (HTTP method) values. An 'operation' element which has no 'verb' attribute is treated as a sub-resource locator - a corresponding resource class has to be available in the model with its 'name' attribute matching the return type's name of this operation.
Every operation can have a number of 'param' elements. A 'param' element should have its 'name' attribute matching a corresponding parameter name in the class resource method. Its 'type' can have the following values : 'PATH', 'QUERY', 'CONTEXT', 'HEADER', 'MATRIX', 'COOKIE', 'FORM' or 'REQUEST_BODY'. Parameters corresponding to response types do not have to be described. It can also have 'defaultValue' and 'encoded' values being set.

Starting from CXF 2.3.2-SNAPSHOT a "oneway" attribute can also be applied to individual operations.

Here is an example :

{code:xml}
<model xmlns="http://cxf.apache.org/jaxrs">
  <resource name="org.apache.cxf.systest.jaxrs.BookStoreNoAnnotations" path="bookstore"
    produces="application/json" consumes="application/json">
    <operation name="getBook" verb="GET" path="/books/{id}" produces="application/xml">
       <param name="id" type="PATH"/>
    </operation>
    <operation name="getBookChapter" path="/books/{id}/chapter">
       <param name="id" type="PATH"/>
    </operation>
    <operation name="updateBook" verb="PUT">
       <param name="book" type="REQUEST_BODY"/>
    </operation>
  </resource>
  <resource name="org.apache.cxf.systest.jaxrs.ChapterNoAnnotations">
    <operation name="getItself" verb="GET"/>
    <operation name="updateChapter" verb="PUT" consumes="application/xml">
        <param name="content" type="REQUEST_BODY"/>
    </operation>
  </resource>
</model>
{code}

This

...

model

...

describes

...

two

...

resources,

...

BookStoreNoAnnotations

...

and

...

ChapterNoAnnotations.

...

The

...

BookStoreNoAnnotations

...

resource

...

has

...

three

...

resource

...

operations,

...

'getBook',

...

'getBookChapter'

...

and

...

'updateBook'.

...

Note

...

that

...

the

...

'getBookChapter'

...

operation

...

element

...

(described

...

in

...

the

...

model)

...

has

...

no

...

'verb'

...

attribute

...

so

...

runtime

...

will

...

identify

...

it

...

as

...

a

...

subresource

...

locator.

...


The

...

runtime

...

will

...

introspect

...

the

...

org.apache.cxf.systest.jaxrs

...

.

...

BookStoreNoAnnotations

...

class

...

and

...

check

...

the

...

return

...

types

...

for

...

both

...

'getBook'

...

and

...

'getBookChapter'

...

methods.

...

BookStoreNoAnnotations.getBookChapter()

...

method's

...

return type is org.apache.

...

cxf.systest.jaxrs.ChapterNoAnnotations so the model will be checked if it contains the resource element with the 'name' attribute equal to 'org.apache.cxf.systest.jaxrs.ChapterNoAnnotations'.

...

After

...

this

...

resource

...

has

...

been

...

found,

...

the

...

ChapterNoAnnotations

...

class

...

is

...

recognized

...

as

...

a

...

sub-resource

...

and

...

then

...

its

...

'getItself'

...

method

...

is

...

checked.

...

Additionally

...

the

...

BookStoreNoAnnotations

...

resource

...

declares

...

that

...

all

...

its

...

resource

...

methods

...

produce

...

'application/json'

...

mediaTypes,

...

while

...

its

...

'getBook'

...

method

...

overrides

...

its

...

with

...

its

...

own

...

'produces'

...

value.

...

BookStoreNoAnnotations

...

resource

...

also

...

has

...

a

...

'consumes'

...

attribute

...

which

...

requires

...

all

...

of

...

the

...

resource

...

methods

...

(such

...

as

...

'updateBook')

...

to

...

consume

...

"application/json"

...

formats.

...

The

...

ChapterNoAnnotations

...

'updateChapter'

...

resource

...

operation

...

requires

...

'application/xml'

...

formats.

...

You

...

can

...

use

...

a

...

comma-

...

separated list

...

of

...

media

...

type

...

values

...

if

...

needed,

...

for

...

example,

...

produces("application/xml;charset=utf-8,application/json")

...

or

...

consumes("application/xml;charset=utf-8,application/json").

Please also see this model file for an example. Providing this file will let all implementations of the interface described in this model instance be exposed as RESTful services supported by the JAX-RS runtime.

Configuration

A user model can be referenced in a number of ways. It can be embedded in a jaxrs:server endpoint definition or linked to through a jaxrs:server modelRef attribute as a classpath resource.

Please see this bean Spring configuration file, look at jaxrs server beans with 'bookservice6' and 'bookservice7' names.

Note that when registering a model from Spring you do not need to declare a jaxrs server serviceBeans section - the runtime will instantiate the beans itself. If you do need to inject certain properties into your service bean from Spring then you do need to declare a service bean too. In this case this bean will be instantiated twice - once by the runtime during the model introspection and once by Spring, however in the end it will be the bean created by Spring that will be used, the one created by the runtime will be removed.
You can avoid this double instantiation by having your model describing the interfaces which the actual root resource beans will implement. In this case only Spring will create a bean and the runtime will apply the model description to this injected bean. Note that if Spring proxifies your bean (for example by applying transaction aspects to it) then the model does have to describe an interface for a match between the model and the injected bean proxy to succeed.

Please have a look at this Spring bean. The jaxrs endpoint with id 'bookservice2' will have BookStoreWithNoAnnotations created twice but it will be the Spring created BookStoreWithNoAnnotations bean that will serve as a resource class instance. The jaxrs endpoint with id 'bookservice3' will have BookStoreWithNoAnnotationsImpl class instantiated only by Spring, with the model describing BookStoreWithNoAnnotationsInterface only that this class implements.

You can also register a model programmatically, for example :

Code Block
java
java
JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
            sf.setAddress("http://localhost:9080/");
String modelRef = "classpath:/org/apache/cxf/systest/jaxrs/resources/resources2.xml";
sf.setModelRef(modelRef);

// or if you have interface classes described in the model already loaded, ex : OSGI
// sf.setModelRefWithServiceClass(modelRef, BookStoreNoAnnotationsInterface.class);

// register an actual bean only if the model describes interfaces
sf.setServiceBeans(new BookStoreNoAnnotationsImpl());

Please also see this system test for the example of how model beans like UserResource can be created and registered programmatically.

Similarly, you can register a user model on the client side, either from jaxrs:client or programmatically, example :

Code Block
java
java
JAXRSClientFactoryBean cf = new JAXRSClientFactoryBean();
cf.setAddress("http://localhost:9080/");
String modelRef = "classpath:/org/apache/cxf/systest/jaxrs/resources/resources2.xml";
sf.setModelRef(modelRef);
BookStoreNoAnnotations proxy = cf.create(BookStoreNoAnnotations.class);

At the moment it is only possible to register a user model with CXFNonSpringJAXRSServlet using the latest 2.2.3-SNAPSHOT like the way it is done in this web.xml. See CXFServlet3 and CXFServlet4 servlet declarations. Note that CXFServlet4 registers a model containing interfaces so it also registers a BookStoreNoAnnotationsImpl service class.

The workaround is to create a custom servlet :

Code Block
java
java
public class JAXRSUserModelServlet extends CXFNonSpringJaxrsServlet  {

    @Override
    public void loadBus(ServletConfig servletConfig) throws ServletException {
        super.loadBus(servletConfig);

        JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
        String address = servletConfig.getInitParameter(SERVICE_ADDRESS_PARAM); //jaxrs.address
        if (address == null) {
            address = "/";
        }
        sf.setAddress(address);

        // modelRef needs to start from 'classpath:', ex 'classpath:/WEB-INF/models/model1.xml
        String modelRef = servletConfig.getInitParameter("user.model");
        sf.setModelRef(modelRef);
        sf.create();
    }
} 

Query String Customization

CXF provides advanced capabilities with respect to query string parsing and expansion.

Collection/List

The typical way to manage collection/list query parameters is described in RFC-6570: URI Template (https://tools.ietf.org/html/rfc6570) and basically assumes the repetition of the name/value pairs, for example: http://localhost:8080/MovieDB/GetJson?name=Actor1&name=Actor2&name=Actor3&startDate=20120101&endDate=2012050

Since 3.1.8+, this behavior could be tweaked using server-side "parse.query.value.as.collection" property,  which adds support for collection/list parameters passed as comma-separated values, , for example: http://localhost:8080/MovieDB/GetJson?name=Actor1,Actor2,Actor3&startDate=20120101&endDate=2012050

The client support was lacking and was added in 3.3.4+ / 3.2.11+ / 3.4.0+ for all types of the clients (WebClient, JAX-RS Client Proxy and Microprofile Client). The matching property name is "expand.query.value.as.collection" and could be specified during the client instance creation, for example:

Code Block
java
java
MyClient client = JAXRSClientFactory
    .create("http://localhost:8080", MyClient.class,
        Collections.singletonMap("expand.query.value.as.collection", "true"));


Code Block
java
java
WebClient client = WebClient.create("http://localhost:8080", 
    Collections.singletonMap("expand.query.value.as.collection", "true"));


Code Block
java
java
MyClient client = RestClientBuilder
    .newBuilder()
    .property("expand.query.value.as.collection", "true")
    .baseUri(new URI("http://localhost:8080"))
    .build(MyClient.class);


Code Block
java
java
WebTarget target = ClientBuilder
    .newClient()
    .property("expand.query.value.as.collection", "true")
    .target("http://localhost:8080");,application/json").

Please also see this [model file|http://svn.apache.org/repos/asf/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/resources/resources2.xml] for an example. Providing this file will let all implementations of the interface described in this model instance be exposed as RESTful services supported by the JAX-RS runtime. 

h2. Configuration 

A user model can be referenced in a number of ways. It can be embedded in a jaxrs:server endpoint definition or linked to through a jaxrs:server modelRef attribute as a classpath resource. 

Please see this [bean|http://svn.apache.org/repos/asf/cxf/trunk/systests/jaxrs/src/test/resources/jaxrs/WEB-INF/beans.xml] Spring configuration file, look at jaxrs server beans with 'bookservice6' and 'bookservice7' names.

Note that when registering a model from Spring you do not need to declare a jaxrs server serviceBeans section - the runtime will instantiate the beans itself. If you do need to inject certain properties into your service bean from Spring then you do need to declare a service bean too. In this case this bean will be instantiated twice - once by the runtime during the model introspection and once by Spring, however in the end it will be the bean created by Spring that will be used, the one created by the runtime will be removed.
You can avoid this double instantiation by having your model describing the interfaces which the actual root resource beans will implement. In this case only Spring will create a bean and the runtime will apply the model description to this injected bean. Note that if Spring proxifies your bean (for example by applying transaction aspects to it) then the model does have to describe an interface for a match between the model and the injected bean proxy to succeed.

Please have a look at [this Spring bean|http://svn.apache.org/repos/asf/cxf/trunk/systests/jaxrs/src/test/resources/jaxrs_proxy/WEB-INF/beans.xml]. The jaxrs endpoint with id 'bookservice2' will have BookStoreWithNoAnnotations created twice but it will be the Spring created BookStoreWithNoAnnotations bean that will serve as a resource class instance. The jaxrs endpoint with id 'bookservice3' will have BookStoreWithNoAnnotationsImpl class instantiated only by Spring, with the model describing BookStoreWithNoAnnotationsInterface only that this class implements.


You can also register a model programmatically, for example :

{code:java}
JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
            sf.setAddress("http://localhost:9080/");
String modelRef = "classpath:/org/apache/cxf/systest/jaxrs/resources/resources2.xml";
sf.setModelRef(modelRef);

// or if you have interface classes described in the model already loaded, ex : OSGI
// sf.setModelRefWithServiceClass(modelRef, BookStoreNoAnnotationsInterface.class);

// register an actual bean only if the model describes interfaces
sf.setServiceBeans(new BookStoreNoAnnotationsImpl());
{code}

Please also see [this system test|http://svn.apache.org/repos/asf/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerUserResourceTest.java] for the example of how model beans like UserResource can be created and registered programmatically.

Similarly, you can register a user model on the client side, either from jaxrs:client or programmatically, example :

{code:java}
JAXRSClientFactoryBean cf = new JAXRSClientFactoryBean();
cf.setAddress("http://localhost:9080/");
String modelRef = "classpath:/org/apache/cxf/systest/jaxrs/resources/resources2.xml";
sf.setModelRef(modelRef);
BookStoreNoAnnotations proxy = cf.create(BookStoreNoAnnotations.class);
{code}

At the moment it is only possible to register a user model with CXFNonSpringJAXRSServlet using the latest 2.2.3-SNAPSHOT like the way it is done in this [web.xml|http://svn.apache.org/repos/asf/cxf/trunk/systests/jaxrs/src/test/resources/jaxrs_non_spring/WEB-INF/web.xml]. See CXFServlet3 and CXFServlet4 servlet declarations. Note that CXFServlet4 registers a model containing interfaces so it also registers a BookStoreNoAnnotationsImpl service class.

The workaround is to create a custom servlet :

{code:java}
public class JAXRSUserModelServlet extends CXFNonSpringJaxrsServlet  {

@Override
public void loadBus(ServletConfig servletConfig) throws ServletException {

super.loadBus(servletConfig);

JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean();
String address = servletConfig.getInitParameter(SERVICE_ADDRESS_PARAM); //jaxrs.address
if (address == null) {
address = "/";
}
sf.setAddress(address);

// modelRef needs to start from 'classpath:', ex 'classpath:/WEB-INF/models/model1.xml
String modelRef = servletConfig.getInitParameter("user.model");
sf.setModelRef(modelRef);
sf.create();
}
{code}