cxf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From conflue...@apache.org
Subject [CONF] Apache CXF Documentation > JAX-RS Advanced Features
Date Fri, 30 Nov 2012 16:22:00 GMT
<html>
<head>
    <base href="https://cwiki.apache.org/confluence">
            <link rel="stylesheet" href="/confluence/s/2042/9/1/_/styles/combined.css?spaceKey=CXF20DOC&amp;forWysiwyg=true" type="text/css">
    </head>
<body style="background: white;" bgcolor="white" class="email-body">
<div id="pageContent">
<div id="notificationFormat">
<div class="wiki-content">
<div class="email">
    <h2><a href="https://cwiki.apache.org/confluence/display/CXF20DOC/JAX-RS+Advanced+Features">JAX-RS Advanced Features</a></h2>
    <h4>Page <b>edited</b> by             <a href="https://cwiki.apache.org/confluence/display/~sergey_beryozkin">Sergey Beryozkin</a>
    </h4>
        <br/>
                         <h4>Changes (1)</h4>
                                 
    
<div id="page-diffs">
                    <table class="diff" cellpadding="0" cellspacing="0">
    
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >The current limitation is that no typed Date queries are supported yet (except for the equality match), for example, &quot;find all the documents issued before a given date&quot;, to be supported shortly. <br> <br></td></tr>
            <tr><td class="diff-added-lines" style="background-color: #dfd;">h3. LDAP <br> <br>Mapping of FIQL expressions to LDAP queries as defined by [RFC-4515|http://tools.ietf.org/html/rfc4515] is supported starting from CXF 2.7.1 with the help of org.apache.cxf.jaxrs.ext.search.ldap.LdapQueryVisitor. Use this visitor when working with LDAP or OSGI. <br> <br>Here is a summary of LDAP filter operators: <br> <br>|| Operator || Description || <br>| &quot;=&quot; | Equal | <br>| &quot;!&quot; | Not Equal | <br>| &quot;&lt;=&quot; | Less Or Equal | <br>| &quot;&gt;=&quot; | Greater or Equal | <br>| &quot;&amp;&quot; | AND | <br>| &quot;\|&quot; | OR | <br> <br>FIQL &quot;=le=&quot; and &quot;=lt=&quot; will both map to &quot;&lt;=&quot;, while &quot;=ge=&quot; and &quot;=gt=&quot; to &quot;&gt;=&quot;. <br> <br>For example: <br> <br>|| FIQL || LDAP || <br>| &quot;name==bar*&quot; | &quot;(name=bar*)&quot; | <br>| &quot;name!=bar&quot; | &quot;(!name=bar)&quot; | <br>| &quot;name!=bar;id=gt=10&quot; | &quot;(&amp;(!name=bar)(id&gt;=10))&quot; | <br>| &quot;name!=bar;(id=gt=10,id=lt=5)&quot; | &quot;(&amp;(!name=bar)(\|(id&gt;=10)(id&lt;=5)))&quot; | <br> <br>The converter is created like all other converters: <br>{code:java} <br> <br>// FIQL &quot;oclass=Bar&quot; <br> <br>// map &#39;oclass&#39; used in the FIQL query to the actual property name, &#39;objectClass&#39; <br>LdapQueryVisitor&lt;Condition&gt; visitor =  <br>   new LdapQueryVisitor&lt;Condition&gt;(Collections.singletonMap(&quot;oclass&quot;, &quot;objectClass&quot;)); <br> <br>filter.accept(visitor.visitor()); <br>String ldap = visitor.getQuery(); <br> <br>{code} <br> <br></td></tr>
            <tr><td class="diff-unchanged" >h3. Custom visitors <br> <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
    
            </table>
    </div>                            <h4>Full Content</h4>
                    <div class="notificationGreySide">
        <p><span style="font-size:2em;font-weight:bold">JAX-RS : Advanced Features</span></p>

<div>
<ul>
    <li><a href='#JAX-RSAdvancedFeatures-JMSSupport'>JMS Support</a></li>
<ul>
    <li><a href='#JAX-RSAdvancedFeatures-Endpoints'>Endpoints</a></li>
    <li><a href='#JAX-RSAdvancedFeatures-Client'>Client</a></li>
</ul>
    <li><a href='#JAX-RSAdvancedFeatures-FIQLsearchqueries'>FIQL search queries</a></li>
<ul>
    <li><a href='#JAX-RSAdvancedFeatures-Introduction'>Introduction</a></li>
    <li><a href='#JAX-RSAdvancedFeatures-WhentouseFIQL'>When to use FIQL</a></li>
    <li><a href='#JAX-RSAdvancedFeatures-DependenciesandConfiguration'>Dependencies and Configuration</a></li>
    <li><a href='#JAX-RSAdvancedFeatures-WorkingwithFIQLqueries'>Working with FIQL queries</a></li>
    <li><a href='#JAX-RSAdvancedFeatures-CapturingFIQLqueries'>Capturing FIQL queries</a></li>
<ul>
    <li><a href='#JAX-RSAdvancedFeatures-Mappingofquerypropertiestobeanproperties'>Mapping of query properties to bean properties</a></li>
</ul>
    <li><a href='#JAX-RSAdvancedFeatures-Mappingofquerypropertiestocolumn%2Ffieldnames'>Mapping of query properties to column/field names</a></li>
    <li><a href='#JAX-RSAdvancedFeatures-SearchBean'>SearchBean</a></li>
    <li><a href='#JAX-RSAdvancedFeatures-ConvertingFIQLqueries'>Converting FIQL queries</a></li>
<ul>
    <li><a href='#JAX-RSAdvancedFeatures-SQL'>SQL</a></li>
    <li><a href='#JAX-RSAdvancedFeatures-JPA2.0'>JPA 2.0</a></li>
    <li><a href='#JAX-RSAdvancedFeatures-Lucene'>Lucene</a></li>
    <li><a href='#JAX-RSAdvancedFeatures-LDAP'>LDAP</a></li>
    <li><a href='#JAX-RSAdvancedFeatures-Customvisitors'>Custom visitors</a></li>
<ul>
    <li><a href='#JAX-RSAdvancedFeatures-Untypedconverters'>Untyped converters</a></li>
    <li><a href='#JAX-RSAdvancedFeatures-Typedconverters'>Typed converters</a></li>
    <li><a href='#JAX-RSAdvancedFeatures-Customparsing'>Custom parsing</a></li>
</ul>
</ul>
    <li><a href='#JAX-RSAdvancedFeatures-ConvertingFIQLquerieswithQueryContext'>Converting FIQL queries with QueryContext</a></li>
    <li><a href='#JAX-RSAdvancedFeatures-SearchExpressionsinURIPathsegments'>Search Expressions in URI Path segments</a></li>
    <li><a href='#JAX-RSAdvancedFeatures-Queriesinvolvingmultipleentities'>Queries involving multiple entities</a></li>
<ul>
    <li><a href='#JAX-RSAdvancedFeatures-Basicqueries'>Basic queries</a></li>
    <li><a href='#JAX-RSAdvancedFeatures-Complexqueries'>Complex queries</a></li>
</ul>
    <li><a href='#JAX-RSAdvancedFeatures-BuildingFIQLqueries'>Building FIQL queries</a></li>
    <li><a href='#JAX-RSAdvancedFeatures-Usingdatesinqueries'>Using dates in queries</a></li>
    <li><a href='#JAX-RSAdvancedFeatures-Alternativequerylanguages'>Alternative query languages</a></li>
</ul>
    <li><a href='#JAX-RSAdvancedFeatures-Onewayinvocations'>Oneway invocations</a></li>
    <li><a href='#JAX-RSAdvancedFeatures-SupportforContinuations'>Support for Continuations</a></li>
    <li><a href='#JAX-RSAdvancedFeatures-Serversidecaching'>Server-side caching</a></li>
    <li><a href='#JAX-RSAdvancedFeatures-RESTfulserviceswithoutannotations'>RESTful services without annotations</a></li>
<ul>
    <li><a href='#JAX-RSAdvancedFeatures-Configuration'>Configuration</a></li>
</ul>
</ul></div>

<h1><a name="JAX-RSAdvancedFeatures-JMSSupport"></a>JMS Support</h1>

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

<h2><a name="JAX-RSAdvancedFeatures-Endpoints"></a>Endpoints</h2>

<p>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:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-xml">
<span class="code-tag">&lt;jaxrs:server serviceName=<span class="code-quote">"s:BookService"</span> transportId=<span class="code-quote">"http://cxf.apache.org/transports/jms"</span> address=<span class="code-quote">"/"</span>&gt;</span>
 <span class="code-tag">&lt;jaxrs:serviceBeans&gt;</span>
   <span class="code-tag">&lt;bean class=<span class="code-quote">"org.apache.cxf.systest.jaxrs.JMSBookStore"</span>/&gt;</span>
 <span class="code-tag">&lt;/jaxrs:serviceBeans&gt;</span>
<span class="code-tag">&lt;/jaxrs:server&gt;</span>
</pre>
</div></div> 

<p>Additionally, JMS queue or topic <a href="http://cxf.apache.org/docs/using-the-jmsconfigfeature.html" class="external-link" rel="nofollow">configuration</a> needs to be done, for example, please see this <a href="http://svn.apache.org/repos/asf/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/resources/jms_server_config.xml" class="external-link" rel="nofollow">beans.xml</a>. Please note how a serviceName attribute is used to specify a service QName for a jaxrs endpoint (default is {<a href="http://reverse.package.name" class="external-link" rel="nofollow">http://reverse.package.name</a>}ServiceClassName), this service name is <br/>
used to configure a jms destination.</p>

<p>Here is the actual <a href="http://svn.apache.org/repos/asf/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSJmsTest.java" class="external-link" rel="nofollow">test</a>. </p>

<p>Here are JMS properties which can help with matching a required method on the JAXRS endpoint :</p>
<ul class="alternate" type="square">
	<li>"Content-Type" : default is "text/xml"</li>
	<li>"Accept" : default is "<b>/</b>"</li>
	<li>"OnewayMessage" : default is "false"</li>
	<li>"org.apache.cxf.message.Message.REQUEST_URI" : default is "/"</li>
	<li>"org.apache.cxf.message.Message.HTTP_REQUEST_METHOD" : default is "POST"</li>
</ul>


<p>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. </p>

<p>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<br/>
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.</p>

<p>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 <a href="http://svn.apache.org/repos/asf/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/ProtocolHeaders.java" class="external-link" rel="nofollow">ProtocolHeaders</a> context which will let you get either HTTP or JMS headers. </p>

<h2><a name="JAX-RSAdvancedFeatures-Client"></a>Client</h2>

<p>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:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-comment">// setup the the client
</span><span class="code-object">String</span> endpointAddressUrlEncoded = <span class="code-quote">"jms:jndi:dynamicQueues/test.jmstransport.text"</span>
             + <span class="code-quote">"?jndiInitialContextFactory=org.apache.activemq.jndi.ActiveMQInitialContextFactory"</span>
             + <span class="code-quote">"&amp;replyToName=dynamicQueues/test.jmstransport.response"</span>
             + <span class="code-quote">"&amp;jndiURL=tcp:<span class="code-comment">//localhost:"</span> + JMS_PORT
</span>             + <span class="code-quote">"&amp;jndiConnectionFactoryName=ConnectionFactory"</span>;
               
JMSBookStore client = JAXRSClientFactory.create(endpointAddressUrlEncoded, JMSBookStore.class);
Book book = client.getBook(<span class="code-quote">"123"</span>);
assertEquals(<span class="code-quote">"Get a wrong response code."</span>, 200, WebClient.client(client).getResponse().getStatus());
assertEquals(<span class="code-quote">"Get a wrong book id."</span>, 123, book.getId());
</pre>
</div></div>

<p>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.</p>


<h1><a name="JAX-RSAdvancedFeatures-FIQLsearchqueries"></a>FIQL search queries</h1>

<h2><a name="JAX-RSAdvancedFeatures-Introduction"></a>Introduction</h2>

<p>CXF JAXRS (since 2.3.0) supports <a href="http://tools.ietf.org/html/draft-nottingham-atompub-fiql-00" class="external-link" rel="nofollow">Feed Item Query Language</a>(FIQL). FIQL provides for a way to express complex search expressions using an intuitive and URI friendly language.</p>

<p>For example, the following query</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
?_s=name==CXF;version=ge=2.2
</pre>
</div></div>

<p>lets users search for all the Apache projects with the name 'CXF' and the version greater or equal 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.<br/>
An expression such as "name==CXF*" can be used to the partial equality checks (in this example: the name should start from "CXF").</p>

<p>More complex composite expressions can also be expressed easily enough, examples:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-comment">// Find all employees younger than 25 or older than 35 living in London
</span>/employees?_s=(age=lt=25,age=gt=35);city==London

<span class="code-comment">// Find all books on math or physics published in 1999 only.
</span>/books?_s=date=lt=2000-01-01;date=gt=1999-01-01;(sub==math,sub==physics)

</pre>
</div></div>

<p>Here is a summary of FIQL operators:</p>

<div class='table-wrap'>
<table class='confluenceTable'><tbody>
<tr>
<th class='confluenceTh'> Operator </th>
<th class='confluenceTh'> Description </th>
</tr>
<tr>
<td class='confluenceTd'> "==" </td>
<td class='confluenceTd'> Equal </td>
</tr>
<tr>
<td class='confluenceTd'> "!=" </td>
<td class='confluenceTd'> Not Equal </td>
</tr>
<tr>
<td class='confluenceTd'> "=lt=" </td>
<td class='confluenceTd'> Less Than </td>
</tr>
<tr>
<td class='confluenceTd'> "=le=" </td>
<td class='confluenceTd'> Less or Equal </td>
</tr>
<tr>
<td class='confluenceTd'> "=gt=" </td>
<td class='confluenceTd'> Greater Than </td>
</tr>
<tr>
<td class='confluenceTd'> "=ge=" </td>
<td class='confluenceTd'> Greater or Equal </td>
</tr>
<tr>
<td class='confluenceTd'> ";" </td>
<td class='confluenceTd'> AND </td>
</tr>
<tr>
<td class='confluenceTd'> "," </td>
<td class='confluenceTd'> OR </td>
</tr>
</tbody></table>
</div>


<p>The last two operators, ","(OR) and ";"(AND) are used to concatenate and build composite (possibly nested) expressions, while the first 6 operators are used to build so called primitive expressions. </p>

<p>As you can see FIQL is rich enough for the service implementations to offer a more interesting search experience around the well-known data, while still keeping the complexity of URI expressions under control which makes it simpler to share such URI queries as well as use the same query language no matter what data store is used internally by the service. </p>

<p>Note, when passing the FIQL queries via URI query parameters,  either '_search' or '_s' query parameter has to be used to mark a FIQL expression for it not to 'interfere' with other optional query parameters. Alternatively the expressions can be encoded as URI path segments, see the sections below for more information.</p>

<h2><a name="JAX-RSAdvancedFeatures-WhentouseFIQL"></a>When to use FIQL</h2>

<p>Consider a typical query expression such as "a=avalue&amp;c=cvalue". This can mean either "find all resources with 'a' and 'c' properties equal to 'avalue' and 'cvalue'" or "find all resources with 'a' or 'c' properties equal to 'avalue' and 'cvalue'". It is application specific on whether it is "and" or "or" as far as the combination of multiple query properties is concerned.</p>

<p>It is also difficult to capture conditional expressions with the custom language, example, "find all resource with 'a' property less than 123".</p>

<p>Use FIQL for capturing simple or medium complexity queries, typically in cases where a set of properties that a user can specify is well-known. Example, a book store resource will let users search books given a number of useful properties(those of Book and/or Library a given book is available in, etc). </p>





<h2><a name="JAX-RSAdvancedFeatures-DependenciesandConfiguration"></a>Dependencies and Configuration</h2>

<p>The following dependency is required starting from CXF 2.6.0:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-xml">
   <span class="code-tag">&lt;dependency&gt;</span>
      <span class="code-tag">&lt;groupId&gt;</span>org.apache.cxf<span class="code-tag">&lt;/groupId&gt;</span>
      <span class="code-tag">&lt;artifactId&gt;</span>cxf-rt-rs-extension-search<span class="code-tag">&lt;/artifactId&gt;</span>
      <span class="code-tag">&lt;version&gt;</span>2.6.0<span class="code-tag">&lt;/version&gt;</span>
   <span class="code-tag">&lt;/dependency&gt;</span>
</pre>
</div></div>

<p>Additionally, in CXF 2.6.0, <a href="http://svn.apache.org/repos/asf/cxf/trunk/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/SearchContextProvider.java" class="external-link" rel="nofollow">SearchContextProvider</a> needs to be registered as jaxrs:provider. </p>

<h2><a name="JAX-RSAdvancedFeatures-WorkingwithFIQLqueries"></a>Working with FIQL queries</h2>

<p>To work with FIQL queries, a <a href="http://svn.apache.org/repos/asf/cxf/trunk/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/SearchContext.java" class="external-link" rel="nofollow">SearchContext</a> needs be injected into an application code and used to retrieve a <a href="http://svn.apache.org/repos/asf/cxf/trunk/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/SearchCondition.java" class="external-link" rel="nofollow">SearchCondition</a> representing the current FIQL query. This SearchCondition can be used in a number of ways for finding the matching data. </p>

<p>In this section we assume that the data to be matched are already available in memory. The follow-up section on converting the queries will show how FIQL queries can be converted to some other query language typed or text expression.</p>

<p>So, suppose a list or map of Book instances is available. Here is one possible approach:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
@Path(<span class="code-quote">"books"</span>)
<span class="code-keyword">public</span> class Books {

<span class="code-keyword">private</span> Map&lt;<span class="code-object">Long</span>, Book&gt; books;
@Context
<span class="code-keyword">private</span> SearchContext context;

 @GET
 <span class="code-keyword">public</span> List&lt;Book&gt; getBook() {

   SearchCondition&lt;Book&gt; sc = searchContext.getCondition(Book.class);
   <span class="code-comment">// SearchCondition#isMet method can also be used to build a list of matching beans
</span>
   <span class="code-comment">// iterate over all the values in the books map and <span class="code-keyword">return</span> a collection of matching beans
</span>   List&lt;Book&gt; found = sc.findAll(books.values());
   <span class="code-keyword">return</span> found;
 }
}
</pre>
</div></div>

<p>Note that a searchContext.getCondition(Book.class) call may return an arbitrary complex SearchCondition, it can be a simple primitive<br/>
expression or a more complex, composite one. </p>

<h2><a name="JAX-RSAdvancedFeatures-CapturingFIQLqueries"></a>Capturing FIQL queries </h2>

<p>For the query expression to be captured, a bean like Book.class is instantiated and has all the search properties injected into it. A complex composite expression will be 'injected' into a number of Book instances - something that may have to be optimized.</p>

<p>Note that by default, a bean such as Book class needs to have a matching property per every property name found in the FIQL expression, for example, given a 'name==b;id==123' expression, the Book class would need to have 'name' and 'id' properties available. The reason for this strict mode being enabled by default is that ignoring a property which can not be captured may lead to a false or unexpected match, for example, if Book 'name' property has been renamed to 'title' then ignoring the 'name' property will lead to a wider match. Thus, if the property does not exist, org.apache.cxf.jaxrs.ext.search.PropertyNotFoundException will be thrown; capturing it can let returning an empty response or retry with the more lax mode, see the next paragraph.</p>

<p>When a more lax parsing of FIQL expressions is expected, for example, where the primitive expressions are joined by "OR",  using SearchBean (see one of the next subsections) or setting a contextual property "search.lax.property.match" will help. The former option is better when you need to know the list of all the properties which have been used in the expression, even those which will not be possible to use for the actual search; the latter option will simply have the unrecognized properties ignored.</p>

<h3><a name="JAX-RSAdvancedFeatures-Mappingofquerypropertiestobeanproperties"></a>Mapping of query properties to bean properties</h3>

<p>As noted above, when a 'typed' bean such as Book.class is used to capture the expressions, a property found in the query expression that can not be mapped to a specific Book property will lead to an exception being reported or it can be optionally ignored. In the reality, there is a number of reasons why the direct match between properties found in query expressions and in capturing beans may not be ideal:</p>

<ul class="alternate" type="square">
	<li>Capturing beans may evolve independently of the actual queries; for example, a working query such as "name==b" will break if a Book 'name' gets renamed to 'title' which will make it difficult to have the queries bookmarked.</li>
	<li>Direct match will simply not work for cases where an actual bean property does not belong to the capturing bean itself but to one of its child properties; for example, a JPA2 Book entity may have an OwnerInfo bean with Name bean property which does contain a primitive 'name' property.</li>
</ul>


<p>The preferred approach, when working with typed beans, is to register a bean properties map, using a "search.bean.property.map" contextual property. For example, given</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">

<span class="code-keyword">public</span> class Book {

    <span class="code-keyword">private</span> <span class="code-object">int</span> id;
    <span class="code-keyword">private</span> OwnerInfo ownerinfo;
    <span class="code-comment">//setters and getters omitted <span class="code-keyword">for</span> brewity
</span>}

@Embeddable
<span class="code-keyword">public</span> class OwnerInfo {

    <span class="code-keyword">private</span> Address address;
    <span class="code-keyword">private</span> Name name;
    <span class="code-comment">//setters and getters omitted <span class="code-keyword">for</span> brewity
</span>}

@Embeddable
<span class="code-keyword">public</span> class Name {

    <span class="code-keyword">private</span> <span class="code-object">String</span> name;
    <span class="code-comment">//setters and getters omitted <span class="code-keyword">for</span> brewity
</span>}

</pre>
</div></div>

<p>and the following map:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-xml">
<span class="code-tag">&lt;map&gt;</span>
 <span class="code-tag"><span class="code-comment">&lt;!-- 'oname' is alias for the actual nested bean property --&gt;</span></span>
 <span class="code-tag">&lt;entry key=<span class="code-quote">"oname"</span> value=<span class="code-quote">"ownerinfo.name.name"</span>/&gt;</span>
<span class="code-tag">&lt;/map&gt;</span>
</pre>
</div></div>

<p>will let users type and bookmark queries (and without seeing them producing unexpected results) like this one:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-comment">//Find all the books owned by Fred with id greater than 100
</span>/books?_s=id=gt=100;oname=Fred
</pre>
</div></div>  

<p>Note, a property name such as "ownerinfo.name.name" uses '.' to let the parser navigate to the actual Name bean which has a 'name' property. This can be optimized in cases where the owner bean is known to have either a constructor or static valueOf() method accepting the 'name' property, for example, given</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">public</span> class Name {

    <span class="code-keyword">private</span> <span class="code-object">String</span> name;
    <span class="code-keyword">public</span> Name() {
    } 
    <span class="code-keyword">public</span> Name(<span class="code-object">String</span> name) {
        <span class="code-keyword">this</span>.name = name;
    }
    <span class="code-comment">//setters and getters omitted <span class="code-keyword">for</span> brewity
</span>}
</pre>
</div></div>
<p>the mapping between "oname" and "ownerinfo.name" will work too.</p>

<h2><a name="JAX-RSAdvancedFeatures-Mappingofquerypropertiestocolumn%2Ffieldnames"></a>Mapping of query properties to column/field names</h2>

<p>When converting FIQL queries to SQL or other untyped query language expressions, as well as when using Lucene converter, it can be useful to be able to map between an actual query parameter and the column or field name. All FIQL converters shipped with CXF have constructors accepting a map for mapping the queries to columns/fields. See the next "SearchBean" section for one example.</p>

<p>Note this property is not the same as the one described in the "Mapping of query properties to bean properties" section. The latter (the one described in the previous section) is required for getting FIQL queries captured into typed, domain specific beans like Book, and it can be sufficient for JPA2 which also has annotations like @Column.  </p>


<h2><a name="JAX-RSAdvancedFeatures-SearchBean"></a>SearchBean</h2>

<p>org.apache.cxf.jaxrs.ext.search.SearchBean is a utility bean class which can simplify analyzing the captured FIQL expressions and converting them to the other language expressions, in cases where having to update the bean class such as Book.class with all the properties that may need to be supported is not practical or the properties need to be managed manually. For example:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-comment">// ?_s=<span class="code-quote">"level=gt=10"</span>
</span>SearchCondition&lt;SearchBean&gt; sc = searchContext.getCondition(SearchBean.class);

Map\&lt;, <span class="code-object">String</span>\&gt; fieldMap = <span class="code-keyword">new</span> HashMap\&lt;<span class="code-object">String</span>, <span class="code-object">String</span>\&gt;();
fieldMap.put(<span class="code-quote">"level"</span>, <span class="code-quote">"LEVEL_COLUMN"</span>);

SQLPrinterVisitor&lt;SearchBean&gt; visitor = <span class="code-keyword">new</span> SQLPrinterVisitor&lt;SearchBean&gt;(fieldMap, <span class="code-quote">"table"</span>, <span class="code-quote">"LEVEL_COLUMN"</span>);
sc.visit(visitor);
assertEquals("SELECT LEVEL_COLUMN FROM table 
              WHERE LEVEL_COLUMN &gt; '10'",
              visitor.getResult());
</pre>
</div></div>


<h2><a name="JAX-RSAdvancedFeatures-ConvertingFIQLqueries"></a>Converting FIQL queries</h2>

<p>SearchCondition can also be used to convert the search requirements (originally expressed in FIQL) into other query languages. <br/>
A custom <a href="http://svn.apache.org/repos/asf/cxf/trunk/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/SearchConditionVisitor.java" class="external-link" rel="nofollow">SearchConditionVisitor</a> implementation can be used to convert SearchCondition objects into custom expressions or typed objects. CXF ships visitors for converting expressions to SQL, JPA 2.0 CriteriaQuery or TypedQuery, Lucene Query.</p>

<h3><a name="JAX-RSAdvancedFeatures-SQL"></a>SQL</h3>

<p>org.apache.cxf.jaxrs.ext.search.sql.SQLPrinterVisitor can be used for creating SQL expressions. For example: </p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-comment">// ?_s=<span class="code-quote">"name==ami*;level=gt=10"</span>
</span>SearchCondition&lt;Book&gt; sc = searchContext.getCondition(Book.class);
SQLPrinterVisitor&lt;Book&gt; visitor = <span class="code-keyword">new</span> SQLPrinterVisitor&lt;Book&gt;(<span class="code-quote">"table"</span>);
sc.visit(visitor);
assertEquals("SELECT * FROM table 
              WHERE 
              name LIKE 'ami%' 
              AND 
              level &gt; '10'",
              visitor.getResult());
</pre>
</div></div>

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

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-comment">// ?_s=<span class="code-quote">"level=gt=10"</span>
</span>SearchCondition&lt;Book&gt; sc = searchContext.getCondition(Book.class);

Map&lt;<span class="code-object">String</span>, <span class="code-object">String</span>&gt; fieldMap = <span class="code-keyword">new</span> HashMap&lt;<span class="code-object">String</span>, <span class="code-object">String</span>&gt;();
fieldMap.put(<span class="code-quote">"level"</span>, <span class="code-quote">"LEVEL_COLUMN"</span>);

SQLPrinterVisitor&lt;Book&gt; visitor = <span class="code-keyword">new</span> SQLPrinterVisitor&lt;Book&gt;(fieldMap, <span class="code-quote">"table"</span>, <span class="code-quote">"LEVEL_COLUMN"</span>);
sc.visit(visitor);
assertEquals("SELECT LEVEL_COLUMN FROM table 
              WHERE LEVEL_COLUMN &gt; '10'",
              visitor.getResult());
</pre>
</div></div>

<p>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.</p>

<h3><a name="JAX-RSAdvancedFeatures-JPA2.0"></a>JPA 2.0</h3>

<p>CXF 2.6.4 and CXF 2.7.1 introduce org.apache.cxf.jaxrs.ext.search.jpa.JPATypedQueryVisitor and org.apache.cxf.jaxrs.ext.search.jpa.JPACriteriaQueryVisitor which can be used to capture FIQL expressions into <br/>
javax.persistence.TypedQuery or javax.persistence.criteria.CriteriaQuery objects.</p>

<p>For example, given:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">

<span class="code-keyword">public</span> class Book {

    <span class="code-keyword">private</span> <span class="code-object">String</span> title;
    <span class="code-keyword">private</span> Date date;
    <span class="code-keyword">private</span> OwnerInfo ownerinfo;
    <span class="code-comment">//setters and getters omitted <span class="code-keyword">for</span> brewity
</span>}

@Embeddable
<span class="code-keyword">public</span> class OwnerInfo {

    <span class="code-keyword">private</span> Address address;
    <span class="code-keyword">private</span> Name name;
    <span class="code-comment">//setters and getters omitted <span class="code-keyword">for</span> brewity
</span>}

@Embeddable
<span class="code-keyword">public</span> class Name {

    <span class="code-keyword">private</span> <span class="code-object">String</span> name;
    <span class="code-comment">//setters and getters omitted <span class="code-keyword">for</span> brewity
</span>}

@Embeddable
<span class="code-keyword">public</span> class Address {

    <span class="code-keyword">private</span> <span class="code-object">String</span> street;
    <span class="code-comment">//setters and getters omitted <span class="code-keyword">for</span> brewity
</span>}


</pre>
</div></div>

<p>the following code can be used:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">import</span> javax.persistence.EntityManager;
<span class="code-keyword">import</span> javax.persistence.TypedQuery;

<span class="code-comment">// init EntityManager as required
</span><span class="code-keyword">private</span> EntityManager entityManager;

<span class="code-comment">// Find the books owned by Barry who lives in London, published starting from the first month of 2000 
</span><span class="code-comment">// ?_s=<span class="code-quote">"date=ge=2000-01-01;ownername=barry;address=london"</span>
</span>
<span class="code-comment">// <span class="code-keyword">this</span> map will have to be set as a contextual property on the jaxrs endpoint
</span><span class="code-comment">// it assumes that Book bean has nested OwnerInfo bean with nested Address and Name beans, 
</span><span class="code-comment">// with the latter containing 'street' and 'name' property respectively
</span>
Map&lt;<span class="code-object">String</span>, <span class="code-object">String</span>&gt; beanPropertiesMap = <span class="code-keyword">new</span> HashMap&lt;<span class="code-object">String</span>, <span class="code-object">String</span>&gt;();
beanPropertiesMap.put(<span class="code-quote">"address"</span>, <span class="code-quote">"ownerInfo.address.street"</span>);
beanPropertiesMap.put(<span class="code-quote">"ownername"</span>, <span class="code-quote">"ownerInfo.name.name"</span>);

<span class="code-comment">// the actual application code
</span>SearchCondition&lt;Book&gt; sc = searchContext.getCondition(Book.class);
SearchConditionVisitor&lt;Book, TypedQuery&lt;Book&gt;&gt; visitor = 
    <span class="code-keyword">new</span> JPATypedQueryVisitor&lt;Book&gt;(entityManager, Book.class);
sc.visit(visitor);

TypedQuery&lt;Book&gt; typedQuery = visitor.getQuery();
List&lt;Book&gt; books = typedQuery.getResultList();
</pre>
</div></div>

<p>Using CriteriaQuery is preferred in cases when the actual result has to be shaped into a bean of different type, using one of JPA2 CriteriaBuilder's shape methods (array(), construct() or tuple()). For example:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">

<span class="code-comment">// Find the books owned by Barry who lives in London, published starting from the first month of 2000 
</span><span class="code-comment">// ?_s=<span class="code-quote">"date=ge=2000-01-01;ownername=barry;address=london"</span>
</span>
<span class="code-comment">// <span class="code-keyword">this</span> map will have to be set as a contextual property on the jaxrs endpoint
</span>Map&lt;<span class="code-object">String</span>, <span class="code-object">String</span>&gt; beanPropertiesMap = <span class="code-keyword">new</span> HashMap&lt;<span class="code-object">String</span>, <span class="code-object">String</span>&gt;();
beanPropertiesMap.put(<span class="code-quote">"address"</span>, <span class="code-quote">"ownerInfo.address.street"</span>);
beanPropertiesMap.put(<span class="code-quote">"ownername"</span>, <span class="code-quote">"ownerInfo.name.name"</span>);

<span class="code-comment">// the actual application code
</span><span class="code-comment">// Only Book 'id' and 'title' properties are extracted from the list of found books
</span> 
SearchCondition&lt;Book&gt; sc = searchContext.getCondition(Book.class);
JPACriteriaQueryVisitor&lt;Book, Tuple&gt; visitor = 
    <span class="code-keyword">new</span> JPACriteriaQueryVisitor&lt;Book, Tuple&gt;(entityManager, Book.class, Tuple.class);
sc.visit(visitor);

List&lt;SingularAttribute&lt;Book, ?&gt;&gt; selections = <span class="code-keyword">new</span> LinkedList&lt;SingularAttribute&lt;Book, ?&gt;&gt;();
<span class="code-comment">// Book_ class is generated by JPA2 compiler
</span>selections.add(Book_.id);
selections.add(Book_.title);

visitor.selectTuple(selections);

TypedQuery&lt;Tuple&gt; query = visitor.getQuery();

List&lt;Tuple&gt; tuples = typedQuery.getResultList();
<span class="code-keyword">for</span> (Tuple tuple : tuples) {
  <span class="code-object">int</span> bookId = tuple.get(<span class="code-quote">"id"</span>, <span class="code-object">String</span>.class);
  <span class="code-object">String</span> title = tuple.get(<span class="code-quote">"title"</span>, <span class="code-object">String</span>.class);
  <span class="code-comment">// add bookId &amp; title to the response data
</span>}

</pre>
</div></div> 



<p>Note that JPACriteriaQueryVisitor will automatically set aliases for an expression like "tuple.get('id', String.class)" to work.<br/>
JPACriteriaQueryVisitor will be enhanced to support more of JPA2 advanced constructs in time.</p>


<p>Or, instead of using Tuple, use a capturing bean like BeanInfo:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">

<span class="code-keyword">public</span> <span class="code-keyword">static</span> class BookInfo {
        <span class="code-keyword">private</span> <span class="code-object">int</span> id;
        <span class="code-keyword">private</span> <span class="code-object">String</span> title;

        <span class="code-keyword">public</span> BookInfo() {
            
        }
        
        <span class="code-keyword">public</span> BookInfo(<span class="code-object">Integer</span> id, <span class="code-object">String</span> title) {
            <span class="code-keyword">this</span>.id = id;
            <span class="code-keyword">this</span>.title = title;
        }
        <span class="code-comment">//setters and getters omitted <span class="code-keyword">for</span> brewity
</span> }

<span class="code-comment">// actual application code:
</span>
SearchCondition&lt;Book&gt; sc = searchContext.getCondition(Book.class);
JPACriteriaQueryVisitor&lt;Book, BookInfo&gt; visitor = 
    <span class="code-keyword">new</span> JPACriteriaQueryVisitor&lt;Book, BookInfo&gt;(entityManager, Book.class, BookInfo.class);
sc.visit(visitor);

List&lt;SingularAttribute&lt;Book, ?&gt;&gt; selections = <span class="code-keyword">new</span> LinkedList&lt;SingularAttribute&lt;Book, ?&gt;&gt;();
<span class="code-comment">// Book_ class is generated by JPA2 compiler
</span>selections.add(Book_.id);
selections.add(Book_.title);

visitor.selectConstruct(selections);

TypedQuery&lt;BookInfo&gt; query = visitor.getQuery();

List&lt;BookInfo&gt; bookInfo = typedQuery.getResultList();
<span class="code-keyword">return</span> bookInfo;

</pre>
</div></div>

<p>JPA2 typed converters also support join operations in cases when explicit collections are used, for example, given:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">

@Entity(name = <span class="code-quote">"Book"</span>)
<span class="code-keyword">public</span> class Book {

    <span class="code-keyword">private</span> List&lt;BookReview&gt; reviews = <span class="code-keyword">new</span> LinkedList&lt;BookReview&gt;();
    <span class="code-keyword">private</span> List&lt;<span class="code-object">String</span>&gt; authors = <span class="code-keyword">new</span> LinkedList&lt;<span class="code-object">String</span>&gt;();
    <span class="code-comment">// other properties omitted
</span>
    @OneToMany
    <span class="code-keyword">public</span> List&lt;BookReview&gt; getReviews() {
        <span class="code-keyword">return</span> reviews;
    }

    <span class="code-keyword">public</span> void setReviews(List&lt;BookReview&gt; reviews) {
        <span class="code-keyword">this</span>.reviews = reviews;
    }

    @ElementCollection
    <span class="code-keyword">public</span> List&lt;<span class="code-object">String</span>&gt; getAuthors() {
        <span class="code-keyword">return</span> authors;
    }

    <span class="code-keyword">public</span> void setAuthors(List&lt;<span class="code-object">String</span>&gt; authors) {
        <span class="code-keyword">this</span>.authors = authors;
    }
}

@Entity
<span class="code-keyword">public</span> class BookReview {
    <span class="code-keyword">private</span> Review review;
    <span class="code-keyword">private</span> List&lt;<span class="code-object">String</span>&gt; authors = <span class="code-keyword">new</span> LinkedList&lt;<span class="code-object">String</span>&gt;();
    <span class="code-keyword">private</span> Book book;
    <span class="code-comment">// other properties omitted    
</span>
    <span class="code-keyword">public</span> Review getReview() {
        <span class="code-keyword">return</span> review;
    }

    <span class="code-keyword">public</span> void setReview(Review review) {
        <span class="code-keyword">this</span>.review = review;
    }

    @OneToOne
    <span class="code-keyword">public</span> Book getBook() {
        <span class="code-keyword">return</span> book;
    }

    <span class="code-keyword">public</span> void setBook(Book book) {
        <span class="code-keyword">this</span>.book = book;
    }

    @ElementCollection
    <span class="code-keyword">public</span> List&lt;<span class="code-object">String</span>&gt; getAuthors() {
        <span class="code-keyword">return</span> authors;
    }

    <span class="code-keyword">public</span> void setAuthors(List&lt;<span class="code-object">String</span>&gt; authors) {
        <span class="code-keyword">this</span>.authors = authors;
    }

    <span class="code-keyword">public</span> <span class="code-keyword">static</span> <span class="code-keyword">enum</span> Review {
        GOOD,
        BAD
    }
}

</pre>
</div></div>

<p>the following will find "all the books with good reviews written by Ted":</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">

SearchCondition&lt;Book&gt; filter = <span class="code-keyword">new</span> FiqlParser&lt;Book&gt;(Book.class).parse(<span class="code-quote">"reviews.review==good;reviews.authors==Ted"</span>);
<span class="code-comment">// in practice, map <span class="code-quote">"reviews.review"</span> to <span class="code-quote">"review"</span>, <span class="code-quote">"reviews.authors"</span> to <span class="code-quote">"reviewAuthor"</span> 
</span><span class="code-comment">// and have a simple query like <span class="code-quote">"review==good;reviewAuthor==Ted"</span> instead
</span>
SearchConditionVisitor&lt;Book, TypedQuery&lt;Book&gt;&gt; jpa = <span class="code-keyword">new</span> JPATypedQueryVisitor&lt;Book&gt;(em, Book.class);
filter.accept(jpa);
TypedQuery&lt;Book&gt; query = jpa.getQuery();
<span class="code-keyword">return</span> query.getResultList();

</pre>
</div></div>


<p>org.apache.cxf.jaxrs.ext.search.jpa.JPALanguageVisitor for converting FIQL expressions into JPQL expressions have also been introduced.</p>

<h3><a name="JAX-RSAdvancedFeatures-Lucene"></a>Lucene</h3>

<p>Mapping of FIQL expressions to Lucene (4.0.0-BETA) Query is supported starting from CXF 2.7.1.</p>

<p>org.apache.cxf.jaxrs.ext.search.lucene.LuceneQueryVisitor can be used to support the default (content) field or specific custom field queries.<br/>
Queries for specific terms and phrases are supported. </p>

<p>Example, "find the documents containing a 'text' term":</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">import</span> org.apache.lucene.search.Query;

SearchCondition&lt;SearchBean&gt; filter = <span class="code-keyword">new</span> FiqlParser&lt;SearchBean&gt;(SearchBean.class).parse(<span class="code-quote">"ct==text"</span>);
LuceneQueryVisitor&lt;SearchBean&gt; lucene = <span class="code-keyword">new</span> LuceneQueryVisitor&lt;SearchBean&gt;(<span class="code-quote">"ct"</span>, <span class="code-quote">"contents"</span>);
lucene.visit(filter);
org.apache.lucene.search.Query termQuery = lucene.getQuery();
<span class="code-comment">// use Query</span>
</pre>
</div></div>

<p>Note, "new LuceneQueryVisitor&lt;SearchBean&gt;("ct", "contents");" is a simple constructor which lets create a mapping between the "ct" name used in the query and the actual default content field. It is not required to use this mapping but it is recommended as it keeps the query expression shorter and does not leak the actual internal Lucene field name.</p>

<p>All the FIQL operators have been mapped to related Lucene Query objects. Queries such as "Less than", or "Greater than and less than" will work fine against the typed fields like "org.apache.lucene.document.IntField". The visitor can be configured with a "primitiveFieldTypeMap" map property to help it map a given query name, example "id" to Integer.class.</p>

<p>Phrases are supported too. Suppose you have few documents with each of them containing name and value pairs like "name=Fred", "name=Barry" and you'd like to list only the documents containing "name=Fred":</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">

SearchCondition&lt;SearchBean&gt; filter = <span class="code-keyword">new</span> FiqlParser&lt;SearchBean&gt;(SearchBean.class).parse(<span class="code-quote">"name==Fred"</span>);
LuceneQueryVisitor&lt;SearchBean&gt; lucene = <span class="code-keyword">new</span> LuceneQueryVisitor&lt;SearchBean&gt;(<span class="code-quote">"contents"</span>);
lucene.visit(filter);
org.apache.lucene.search.Query phraseQuery = lucene.getQuery();
<span class="code-comment">// use query</span>
</pre>
</div></div>

<p>In this example, the visitor is requested to create Lucene org.apache.lucene.search.PhraseQuery against the specified contents field ("contents"). The visitor can also accept a contentsFieldMap map property when different phrases may need to be checked against different contents fields.</p>


<p>The current limitation is that no typed Date queries are supported yet (except for the equality match), for example, "find all the documents issued before a given date", to be supported shortly.</p>

<h3><a name="JAX-RSAdvancedFeatures-LDAP"></a>LDAP</h3>

<p>Mapping of FIQL expressions to LDAP queries as defined by <a href="http://tools.ietf.org/html/rfc4515" class="external-link" rel="nofollow">RFC-4515</a> is supported starting from CXF 2.7.1 with the help of org.apache.cxf.jaxrs.ext.search.ldap.LdapQueryVisitor. Use this visitor when working with LDAP or OSGI.</p>

<p>Here is a summary of LDAP filter operators:</p>

<div class='table-wrap'>
<table class='confluenceTable'><tbody>
<tr>
<th class='confluenceTh'> Operator </th>
<th class='confluenceTh'> Description </th>
</tr>
<tr>
<td class='confluenceTd'> "=" </td>
<td class='confluenceTd'> Equal </td>
</tr>
<tr>
<td class='confluenceTd'> "!" </td>
<td class='confluenceTd'> Not Equal </td>
</tr>
<tr>
<td class='confluenceTd'> "&lt;=" </td>
<td class='confluenceTd'> Less Or Equal </td>
</tr>
<tr>
<td class='confluenceTd'> "&gt;=" </td>
<td class='confluenceTd'> Greater or Equal </td>
</tr>
<tr>
<td class='confluenceTd'> "&amp;" </td>
<td class='confluenceTd'> AND </td>
</tr>
<tr>
<td class='confluenceTd'> "&#124;" </td>
<td class='confluenceTd'> OR </td>
</tr>
</tbody></table>
</div>


<p>FIQL "=le=" and "=lt=" will both map to "&lt;=", while "=ge=" and "=gt=" to "&gt;=".</p>

<p>For example:</p>

<div class='table-wrap'>
<table class='confluenceTable'><tbody>
<tr>
<th class='confluenceTh'> FIQL </th>
<th class='confluenceTh'> LDAP </th>
</tr>
<tr>
<td class='confluenceTd'> "name==bar*" </td>
<td class='confluenceTd'> "(name=bar*)" </td>
</tr>
<tr>
<td class='confluenceTd'> "name!=bar" </td>
<td class='confluenceTd'> "(!name=bar)" </td>
</tr>
<tr>
<td class='confluenceTd'> "name!=bar;id=gt=10" </td>
<td class='confluenceTd'> "(&amp;(!name=bar)(id&gt;=10))" </td>
</tr>
<tr>
<td class='confluenceTd'> "name!=bar;(id=gt=10,id=lt=5)" </td>
<td class='confluenceTd'> "(&amp;(!name=bar)(&#124;(id&gt;=10)(id&lt;=5)))" </td>
</tr>
</tbody></table>
</div>


<p>The converter is created like all other converters:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">

<span class="code-comment">// FIQL <span class="code-quote">"oclass=Bar"</span>
</span>
<span class="code-comment">// map 'oclass' used in the FIQL query to the actual property name, 'objectClass'
</span>LdapQueryVisitor&lt;Condition&gt; visitor = 
   <span class="code-keyword">new</span> LdapQueryVisitor&lt;Condition&gt;(Collections.singletonMap(<span class="code-quote">"oclass"</span>, <span class="code-quote">"objectClass"</span>));

filter.accept(visitor.visitor());
<span class="code-object">String</span> ldap = visitor.getQuery();

</pre>
</div></div>

<h3><a name="JAX-RSAdvancedFeatures-Customvisitors"></a>Custom visitors</h3>

<p>In cases when a custom conversion has to be done, a converter for doing the untyped (example, SQL) or typed (example, JPA2 TypedQuery) conversions can be provided. </p>

<h4><a name="JAX-RSAdvancedFeatures-Untypedconverters"></a>Untyped converters</h4>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">public</span> class CustomSQLVisitor&lt;T&gt; <span class="code-keyword">extends</span> AbstractSearchConditionVisitor&lt;T, <span class="code-object">String</span>&gt; {

<span class="code-keyword">private</span> <span class="code-object">String</span> tableName;
<span class="code-keyword">private</span> StringBuilder sb = <span class="code-keyword">new</span> StringBuilder();

<span class="code-keyword">public</span> void visit(SearchCondition&lt;T&gt; sc) {
        
        <span class="code-keyword">if</span> (sb == <span class="code-keyword">null</span>) {
            sb = <span class="code-keyword">new</span> StringBuilder();
            <span class="code-comment">// start the expression as needed, example
</span>            <span class="code-comment">// sb.append(<span class="code-quote">"Select from "</span>).append(tableName);
</span>        }
        
        PrimitiveStatement statement = sc.getStatement();
        <span class="code-keyword">if</span> (statement != <span class="code-keyword">null</span>) {
                <span class="code-comment">// ex <span class="code-quote">"a &gt; b"</span>
</span>                <span class="code-comment">// use statement.getValue()
</span>                <span class="code-comment">// use statement.getConditionType() such as greaterThan, lessThan
</span>                <span class="code-comment">// use statement.getProperty();
</span>                <span class="code-comment">// to convert <span class="code-quote">"a &gt; b"</span> into SQL expression
</span>                sb.append(toSQL(statement));         
        } <span class="code-keyword">else</span> {
            <span class="code-comment">// composite expression, ex <span class="code-quote">"a &gt; b;c &lt; d"</span>
</span>            <span class="code-keyword">for</span> (SearchCondition&lt;T&gt; condition : sc.getSearchConditions()) {
                <span class="code-comment">// pre-process, example sb.append(<span class="code-quote">"("</span>);
</span>                condition.accept(<span class="code-keyword">this</span>);
                <span class="code-comment">// post-process, example sb.append(<span class="code-quote">")"</span>);
</span>            }
        }
    }

    <span class="code-keyword">public</span> <span class="code-object">String</span> getQuery() {
        <span class="code-keyword">return</span> sb.toString();
    }
}
</pre>
</div></div>

<h4><a name="JAX-RSAdvancedFeatures-Typedconverters"></a>Typed converters</h4>

<p>import org.custom.search.Query;</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">public</span> class CustomTypedVisitor&lt;T&gt; <span class="code-keyword">extends</span> AbstractSearchConditionVisitor&lt;T, Query&gt; {

<span class="code-keyword">private</span> Stack&lt;List&lt;Query&gt;&gt; queryStack = <span class="code-keyword">new</span> Stack&lt;List&lt;Query&gt;&gt;();

<span class="code-keyword">public</span> void visit(SearchCondition&lt;T&gt; sc) {
                
        PrimitiveStatement statement = sc.getStatement();
        <span class="code-keyword">if</span> (statement != <span class="code-keyword">null</span>) {
                <span class="code-comment">// ex <span class="code-quote">"a &gt; b"</span>
</span>                <span class="code-comment">// use statement.getValue()
</span>                <span class="code-comment">// use statement.getConditionType() such as greaterThan, lessThan
</span>                <span class="code-comment">// use statement.getProperty();
</span>                <span class="code-comment">// to convert <span class="code-quote">"a &gt; b"</span> into Query object
</span>                Query query = buildSimpleQuery(statement);
                queryStack.peek().add(query);                 

        } <span class="code-keyword">else</span> {
            <span class="code-comment">// composite expression, ex <span class="code-quote">"a &gt; b;c &lt; d"</span>
</span>            queryStack.push(<span class="code-keyword">new</span> ArrayList&lt;Query&gt;());

            <span class="code-keyword">for</span> (SearchCondition&lt;T&gt; condition : sc.getSearchConditions()) {
                condition.accept(<span class="code-keyword">this</span>);
            }

            <span class="code-object">boolean</span> orCondition = sc.getConditionType() == ConditionType.OR;
            List&lt;Query&gt; queries = queryStack.pop();
            queryStack.peek().add(createCompositeQuery(queries, orCondition));
        }
    }

    <span class="code-keyword">public</span> Query getResult() {
        <span class="code-keyword">return</span> queryStack.peek().get(0);
    }
}
</pre>
</div></div>


<h4><a name="JAX-RSAdvancedFeatures-Customparsing"></a>Custom parsing</h4>

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

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">

@Path(<span class="code-quote">"/search"</span>)
<span class="code-keyword">public</span> class SearchEngine {
    @Context
    <span class="code-keyword">private</span> UriInfo ui;

    @GET
    <span class="code-keyword">public</span> List&lt;Book&gt; findBooks() {
        MultivaluedMap&lt;<span class="code-object">String</span>, <span class="code-object">String</span>&gt; params = ui.getQueryParameters();
        <span class="code-object">String</span> fiqlQuery = params.getFirst(<span class="code-quote">"_s"</span>);
        <span class="code-comment">// delegate to your own custom handler 
</span>
        <span class="code-comment">// note that the original search expression can also be retrieved 
</span>        <span class="code-comment">// using a SearchContext.getSearchExpression() method
</span>}

</pre>
</div></div>

<h2><a name="JAX-RSAdvancedFeatures-ConvertingFIQLquerieswithQueryContext"></a>Converting FIQL queries with QueryContext</h2>

<p><a href="http://svn.apache.org/repos/asf/cxf/trunk/rt/rs/extensions/search/src/main/java/org/apache/cxf/jaxrs/ext/search/QueryContext.java" class="external-link" rel="nofollow">QueryContext</a> is the helper context available from CXF 2.7.1 which makes it simpler for the application code to<br/>
get the converted query expression, with the actual converter/visitor registered as the jaxrs contextual property, for example:</p>


<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">import</span> java.util.ArrayList;
<span class="code-keyword">import</span> java.util.List;
<span class="code-keyword">import</span> org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
<span class="code-keyword">import</span> org.apache.cxf.jaxrs.ext.search.QueryContextProvider;
<span class="code-keyword">import</span> org.apache.cxf.jaxrs.ext.search.SearchBean;
<span class="code-keyword">import</span> org.apache.cxf.jaxrs.ext.search.sql.SQLPrinterVisitor;

<span class="code-keyword">import</span> books.BookStore;

<span class="code-comment">// Register the visitor:
</span>JAXRSServerFactoryBean sf = <span class="code-keyword">new</span> JAXRSServerFactoryBean();
List&lt;<span class="code-object">Object</span>&gt; providers = <span class="code-keyword">new</span> ArrayList&lt;<span class="code-object">Object</span>&gt;();
providers.add(<span class="code-keyword">new</span> QueryContextProvider());
sf.setProviders(providers);
sf.getProperties(<span class="code-keyword">true</span>).put(<span class="code-quote">"search.visitor"</span>, <span class="code-keyword">new</span> SQLPrinterVisitor&lt;SearchBean&gt;(<span class="code-quote">"books"</span>));


sf.setResourceClasses(BookStore.class);
server = sf.create();
</pre>
</div></div> 

<p>and convert the queries:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
@Path(<span class="code-quote">"/"</span>)
<span class="code-keyword">public</span> class BookStore { 
    @GET
    @Path(<span class="code-quote">"/books/{expression}"</span>)
    @Produces(<span class="code-quote">"application/xml"</span>)
    <span class="code-keyword">public</span> List&lt;Book&gt; getBookQueryContext(@PathParam(<span class="code-quote">"expression"</span>) <span class="code-object">String</span> expression, 
                                      @Context QueryContext searchContext) 
        <span class="code-keyword">throws</span> BookNotFoundFault {
        <span class="code-object">String</span> sqlExpression = searchContext.getConvertedExpression(expression);
        <span class="code-comment">// pass it to the SQL DB and <span class="code-keyword">return</span> the list of Books
</span>
        <span class="code-comment">// or instead of registering SQLPrinterVisitor, register JPA2TypedQueryVisitor
</span>        <span class="code-comment">// (see section on JPA2) and <span class="code-keyword">do</span>
</span>        TypedQuery&lt;Book&gt; query = searchContext.getConvertedExpression(expression, Book.class, TypedQuery.class);
        <span class="code-keyword">return</span> query.getResultList();   
    }
}
</pre>
</div></div>

<p>where the client code may look like this:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-object">String</span> address = <span class="code-quote">"http:<span class="code-comment">//localhost:8080/bookstore/books/id=ge=123"</span>;
</span>WebClient client = WebClient.create(address);
client.accept(<span class="code-quote">"application/xml"</span>);
List&lt;Book&gt; books = client.getCollection(Book.class);
</pre>
</div></div>

<h2><a name="JAX-RSAdvancedFeatures-SearchExpressionsinURIPathsegments"></a>Search Expressions in URI Path segments</h2>

<p>By default, a FIQL expression is expected to be available in either '_s' or '_search' query.<br/>
For example, "find all the books with an 'id' property value less than 123":</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-xml">
GET /books?_s=id=lt=123
</pre>
</div></div>

<p>Starting from CXF 2.6.2, it is possible to work with FIQL expressions included in URI path segments, for example, the same query can be expressed<br/>
in a number of ways:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-xml">

GET /books/id=lt=123
GET /books[id=lt=123]
GET /books(id=lt=123)
GET /books;id=lt=123

//etc, etc

</pre>
</div></div>

<p>Such expressions can be captured in the code using JAX-RS annotations:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
@Path(<span class="code-quote">"search"</span>)
<span class="code-keyword">public</span> class BooksResource {
   @Context
   <span class="code-keyword">private</span> SearchContext context;

   <span class="code-comment">//GET /books[id=lt=123]
</span>   @GET
   @Path(<span class="code-quote">"books[{search}]"</span>) 
   <span class="code-keyword">public</span> List&lt;Book&gt; findSelectedBooks(@PathParam(<span class="code-quote">"search"</span>) <span class="code-object">String</span> searchExpression) {
       <span class="code-keyword">return</span> doFindSelectedBooks(searchExpression);
   }

   <span class="code-comment">//GET /books(id=lt=123)
</span>   @GET
   @Path(<span class="code-quote">"books({search})"</span>) 
   <span class="code-keyword">public</span> List&lt;Book&gt; findSelectedBooks(@PathParam(<span class="code-quote">"search"</span>) <span class="code-object">String</span> searchExpression) {
       <span class="code-keyword">return</span> doFindSelectedBooks(searchExpression);
   }

   <span class="code-comment">//GET /books/id=lt=123
</span>   @GET
   @Path(<span class="code-quote">"books/{search}"</span>) 
   <span class="code-keyword">public</span> List&lt;Book&gt; findSelectedBooks(@PathParam(<span class="code-quote">"search"</span>) <span class="code-object">String</span> searchExpression) {
       <span class="code-keyword">return</span> doFindSelectedBooks(searchExpression);
   }

   <span class="code-comment">//GET /books;id=lt=123
</span>   @GET
   @Path(<span class="code-quote">"books;{search}"</span>) 
   <span class="code-keyword">public</span> List&lt;Book&gt; findSelectedBooks(@PathParam(<span class="code-quote">"search"</span>) <span class="code-object">String</span> searchExpression) {
       <span class="code-keyword">return</span> doFindSelectedBooks(searchExpression);
   }

   <span class="code-keyword">public</span> List&lt;Book&gt; doFindSelectedBooks(<span class="code-object">String</span> searchExpression) {
       SearchCondition&lt;Book&gt; sc = context.getCondition(searchExpression, Book.class);
   
       <span class="code-comment">// JPA2 enity manager is initialized earlier
</span>       JPATypedQuery&lt;Book&gt; visitor = <span class="code-keyword">new</span> JPATypedQueryVisitor&lt;Book&gt;(entityManager, Book.class);
       sc.visit(visitor);
   
       TypedQuery&lt;Book&gt; typedQuery = visitor.getQuery();
       <span class="code-keyword">return</span> typedQuery.getResultList();
   }

}
</pre>
</div></div> 

<p>Note that if you have an expression added to a URI path segment with a ";" character acting as a separator, example, "/books;id=lt=123",<br/>
or if an expression itself includes ";", example, "/books[id=lt=123;id=gt=300]" ("find all the books with id less than 123 or greater than 300") <br/>
then a boolean contextual property "ignore.matrix.parameters" has to be set to "true" for the runtime to avoid splitting the path segment into the path value and matrix parameters.</p>

<h2><a name="JAX-RSAdvancedFeatures-Queriesinvolvingmultipleentities"></a>Queries involving multiple entities</h2>

<h3><a name="JAX-RSAdvancedFeatures-Basicqueries"></a>Basic queries</h3>

<p>Consider the query like "find chapters with a given chapter id from all the books with 'id' less than 123".<br/>
One easy way to manage such queries is to make FIQL and JAX-RS work together. For example:    </p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
@Path(<span class="code-quote">"search"</span>)
<span class="code-keyword">public</span> class BooksResource {
   @Context
   <span class="code-keyword">private</span> SearchContext context;

   <span class="code-comment">//GET /books[id=lt=123]/chapter/1
</span>   @GET
   @Path(<span class="code-quote">"books[{search}]/chapter/{id}"</span>) 
   <span class="code-keyword">public</span> List&lt;Chapter&gt; findSelectedChapters(@PathParam(<span class="code-quote">"search"</span>) <span class="code-object">String</span> searchExpression,
                                       @PathParam(<span class="code-quote">"id"</span>) <span class="code-object">int</span> chapterIndex) {
       <span class="code-keyword">return</span> doFindSelectedChapters(searchExpression, chapterIndex);
   }

   <span class="code-keyword">public</span> List&lt;Chapter&gt; doFindSelectedChapters(<span class="code-object">String</span> searchExpression, <span class="code-object">int</span> chapterIndex) {
       SearchCondition&lt;Book&gt; sc = context.getCondition(searchExpression, Book.class);
   
       <span class="code-comment">// JPA2 enity manager is initialized earlier
</span>       JPATypedQuery&lt;Book&gt; visitor = <span class="code-keyword">new</span> JPATypedQueryVisitor&lt;Book&gt;(entityManager, Book.class);
       sc.visit(visitor);
   
       TypedQuery&lt;Book&gt; typedQuery = visitor.getQuery();
       List&lt;Book&gt; books = typedQuery.getResultList();

       List&lt;Chapter&gt; chapters = <span class="code-keyword">new</span> ArrayList&lt;Chapter&gt;(books.size);
       <span class="code-keyword">for</span> (Book book : books) {
           chapters.add(book.getChapter(chapterIndex)); 
       }   
       <span class="code-keyword">return</span> chapters;
   }

}
</pre>
</div></div> 

<h3><a name="JAX-RSAdvancedFeatures-Complexqueries"></a>Complex queries</h3>

<p>In the previous section we had the properties of two entities, Book and Chapter, used in the query. The query was considered 'simple' because it was really only the simple book properties that were checked, and the only chapter property was a chapter id, assumed to be equal to a chapter list index.   </p>

<p>Consider "Find all the chapters with id less than 5 for all the books with id greater than 300".</p>

<p>One way to handle is to follow the example from the previous section with few modifications: </p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
@Path(<span class="code-quote">"search"</span>)
<span class="code-keyword">public</span> class BooksResource {
   @Context
   <span class="code-keyword">private</span> SearchContext context;

   <span class="code-comment">//GET /books(id=gt=300)/chapters(id=lt=5)
</span>   @GET
   @Path(<span class="code-quote">"books({search1})/chapter/{search2}"</span>) 
   <span class="code-keyword">public</span> List&lt;Chapter&gt; findSelectedChapters(@PathParam(<span class="code-quote">"search1"</span>) <span class="code-object">String</span> bookExpression,
                                       @PathParam(<span class="code-quote">"search2"</span>) <span class="code-object">String</span> chapterExpression) {
       <span class="code-keyword">return</span> doFindSelectedBooks(bookExpression, chapterExpression);
   }

   <span class="code-keyword">public</span> List&lt;Chapter&gt; doFindSelectedChapters(<span class="code-object">String</span> bookExpression, <span class="code-object">String</span> chapterExpression) {
       <span class="code-comment">// find the books first
</span>       
       SearchCondition&lt;Book&gt; bookCondition = context.getCondition(searchExpression, Book.class);
   
       JPATypedQuery&lt;Book&gt; visitor = <span class="code-keyword">new</span> JPATypedQueryVisitor&lt;Book&gt;(entityManager, Book.class);
       bookCondition.visit(visitor);
       TypedQuery&lt;Book&gt; typedQuery = visitor.getQuery();
       List&lt;Book&gt; books = typedQuery.getResultList();

       <span class="code-comment">// now get the chapters
</span>       SearchCondition&lt;Chapter&gt; chapterCondition = context.getCondition(chapterExpression, Chapter.class);
       List&lt;Chapter&gt; chapters = <span class="code-keyword">new</span> ArrayList&lt;Chapter&gt;();
       <span class="code-keyword">for</span> (Book book : books) {
           chapters.addAll(chapterCondition.findAll(book.getChapters()); 
       }   
       <span class="code-keyword">return</span> chapters;
   }

}
</pre>
</div></div> 

<p>In this case two conditions are created and the 2nd condition is used to filter the chapters from the books filtered by the 1st condition.</p>

<p>Perhaps a simpler approach, especially in case of JPA2, is to start looking for Chapters immediately, assuming Chapter classes have a one to one bidirectional relationship with Book:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">

<span class="code-keyword">public</span> class Chapter {
   <span class="code-keyword">private</span> <span class="code-object">int</span> id;
   <span class="code-keyword">private</span> Book book;

   @OneToOne(mappedBy=<span class="code-quote">"book"</span>)
   <span class="code-keyword">public</span> Book getBook() {}
}

@Path(<span class="code-quote">"search"</span>)
<span class="code-keyword">public</span> class BooksResource {
   @Context
   <span class="code-keyword">private</span> SearchContext context;

   <span class="code-comment">//GET /chapters(bookId=gt=300,id=lt=5)
</span>   @GET
   @Path(<span class="code-quote">"chapters({search})"</span>) 
   <span class="code-keyword">public</span> List&lt;Chapter&gt; findSelectedChapters(@PathParam(<span class="code-quote">"search"</span>) <span class="code-object">String</span> chapterExpression) {
       
       SearchCondition&lt;Chapter&gt; chapterCondition = context.getCondition(chapterExpression, Chapter.class);
   
       JPATypedQuery&lt;Chapter&gt; visitor = <span class="code-keyword">new</span> JPATypedQueryVisitor&lt;Chapter&gt;(entityManager, Chapter.class);
       chapterCondition.visit(visitor);
       TypedQuery&lt;Chapter&gt; typedQuery = visitor.getQuery();
       <span class="code-keyword">return</span> typedQuery.getResultList();
   }

}

Note <span class="code-keyword">this</span> code assumes that <span class="code-quote">"bookId"</span> is mapped to <span class="code-quote">"Book.id"</span> property with the help of the contextual <span class="code-quote">"search.bean.property.map"</span> property as explained earlier.

</pre>
</div></div>

<h2><a name="JAX-RSAdvancedFeatures-BuildingFIQLqueries"></a>Building FIQL queries</h2>

<p>CXF 2.4.0 introduces <a href="http://svn.apache.org/repos/asf/cxf/trunk/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/search/client/SearchConditionBuilder.java" class="external-link" rel="nofollow">SearchConditionBuilder</a> which makes it simpler to build FIQL queries. SearchConditionBuilder is an abstract class that returns a FIQL builder by default:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
SearchConditionBuilder b = SearchConditionBuilder.instance();
<span class="code-object">String</span> fiqlQuery = b.is(<span class="code-quote">"id"</span>).greaterThan(123).query();

WebClient wc = WebClient.create(<span class="code-quote">"http:<span class="code-comment">//books.com/search"</span>);
</span>wc.query(<span class="code-quote">"_s"</span>, fiqlQuery);
<span class="code-comment">// find all the books with id greater than 123 
</span>Collection books = wc.getCollection(Book.class);
</pre>
</div></div>

<p>Here is an example of building more complex queries:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-comment">// OR condition
</span><span class="code-object">String</span> ret = b.is(<span class="code-quote">"foo"</span>).greaterThan(20).or().is(<span class="code-quote">"foo"</span>).lessThan(10).query();
assertEquals(<span class="code-quote">"foo=gt=20,foo=lt=10"</span>, ret);

<span class="code-comment">// AND condition
</span><span class="code-object">String</span> ret = b.is(<span class="code-quote">"foo"</span>).greaterThan(20).and().is(<span class="code-quote">"bar"</span>).equalTo(<span class="code-quote">"plonk"</span>).query();
assertEquals(<span class="code-quote">"foo=gt=20;bar==plonk"</span>, ret);

<span class="code-comment">// Complex condition
</span><span class="code-object">String</span> ret = b.is(<span class="code-quote">"foo"</span>).equalTo(123.4).or().and(
            b.is(<span class="code-quote">"bar"</span>).equalTo(<span class="code-quote">"asadf*"</span>), 
            b.is(<span class="code-quote">"baz"</span>).lessThan(20)).query();
assertEquals(<span class="code-quote">"foo==123.4,(bar==asadf*;baz=lt=20.0)"</span>, ret);
</pre>
</div></div>

<h2><a name="JAX-RSAdvancedFeatures-Usingdatesinqueries"></a>Using dates in queries</h2>

<p>By default, the date values have to have the following <a href="http://download.oracle.com/javase/6/docs/api/java/text/SimpleDateFormat.html" class="external-link" rel="nofollow">format</a>: "yyyy-MM-dd", for example:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
?_search=date=le=2010-03-11
</pre>
</div></div> 

<p>A custom date format can be supported. Use "search.date-format" contextual property, example, "search.date-format"="yyyy-MM-dd'T'HH:mm:ss" will let users type:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
?_search=time=le=2010-03-11T18:00:00
</pre>
</div></div>

<p>If needed, "search.timezone.support" can be enabled to get the timezones supported too.</p>

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

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
Map&lt;<span class="code-object">String</span>, <span class="code-object">String</span>&gt; props = <span class="code-keyword">new</span> HashMap&lt;<span class="code-object">String</span>, <span class="code-object">String</span>&gt;();
props.put(<span class="code-quote">"search.date-format"</span>, <span class="code-quote">"yyyy-MM-dd'T'HH:mm:ss"</span>);
props.put(<span class="code-quote">"search.timezone.support"</span>, <span class="code-quote">"<span class="code-keyword">false</span>"</span>);

Date d = df.parse(<span class="code-quote">"2011-03-01 12:34:00"</span>);
        
FiqlSearchConditionBuilder bCustom = <span class="code-keyword">new</span> FiqlSearchConditionBuilder(props);
        
<span class="code-object">String</span> ret = bCustom.is(<span class="code-quote">"foo"</span>).equalTo(d).query();
assertEquals(<span class="code-quote">"foo==2011-03-01T12:34:00"</span>, ret);
</pre>
</div></div>

<h2><a name="JAX-RSAdvancedFeatures-Alternativequerylanguages"></a>Alternative query languages</h2>

<p>At the moment FIQL is the only and default query language (capable of expressing simple and complex quieries) recognized by the Search module.<br/>
org.apache.cxf.jaxrs.ext.search.SearchConditionParser has been introduced to facilitate a support for the alternative query languages. More work in this area will be done in the future. </p>

<h1><a name="JAX-RSAdvancedFeatures-Onewayinvocations"></a>Oneway invocations</h1>

<p>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.</p>

<h1><a name="JAX-RSAdvancedFeatures-SupportforContinuations"></a>Support for Continuations </h1>

<p>Please see <a href="http://sberyozkin.blogspot.com/2008/12/continuations-in-cxf.html" class="external-link" rel="nofollow">this blog entry</a> describing how JAXRS (and indeed) JAXWS services can rely on the CXF Continuations API. </p>

<p>Please see the <a href="/confluence/pages/createpage.action?spaceKey=CXF20DOC&amp;title=CXF+Continuations&amp;linkCreation=true&amp;fromPageId=24190907" class="createlink">CXF Continuations</a> page for more information.</p>

<h1><a name="JAX-RSAdvancedFeatures-Serversidecaching"></a>Server-side caching</h1>

<p><a href="http://ehcache.org/documentation/web_caching.html" class="external-link" rel="nofollow">Ehcache-Web</a> and other similar frameworks can be used to provide an advanced support for<br/>
the server-side caching.</p>

<p>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':</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-xml">
<span class="code-tag">&lt;context-param&gt;</span>
        <span class="code-tag">&lt;param-name&gt;</span>webAppRootKey<span class="code-tag">&lt;/param-name&gt;</span>
        <span class="code-tag">&lt;param-value&gt;</span>ehcache-cxf<span class="code-tag">&lt;/param-value&gt;</span>
    <span class="code-tag">&lt;/context-param&gt;</span>
<span class="code-tag">&lt;filter&gt;</span>
        <span class="code-tag">&lt;filter-name&gt;</span>SimplePageCachingFilter<span class="code-tag">&lt;/filter-name&gt;</span>
        <span class="code-tag">&lt;filter-class&gt;</span>net.sf.ehcache.constructs.web.filter.SimplePageCachingFilter<span class="code-tag">&lt;/filter-class&gt;</span>
        <span class="code-tag">&lt;init-param&gt;</span>
            <span class="code-tag">&lt;param-name&gt;</span>varyHeader<span class="code-tag">&lt;/param-name&gt;</span>
            <span class="code-tag">&lt;param-value&gt;</span>true<span class="code-tag">&lt;/param-value&gt;</span>
        <span class="code-tag">&lt;/init-param&gt;</span>
    <span class="code-tag">&lt;/filter&gt;</span>
    
    <span class="code-tag">&lt;filter-mapping&gt;</span>
        <span class="code-tag">&lt;filter-name&gt;</span>SimplePageCachingFilter<span class="code-tag">&lt;/filter-name&gt;</span>
        <span class="code-tag">&lt;url-pattern&gt;</span>/*<span class="code-tag">&lt;/url-pattern&gt;</span>
    <span class="code-tag">&lt;/filter-mapping&gt;</span>
</pre>
</div></div> 

<p>Please see the <a href="http://ehcache.org/documentation/web_caching.html" class="external-link" rel="nofollow">Ehcache-Web</a> page for more information on how to configure it, here is one example:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-xml">
&lt;ehcache <span class="code-keyword">xmlns:xsi</span>=<span class="code-quote">"http://www.w3.org/2001/XMLSchema-instance"</span>
    xsi:noNamespaceSchemaLocation=<span class="code-quote">"../../main/config/ehcache.xsd"</span>
    updateCheck=<span class="code-quote">"false"</span>
    monitoring=<span class="code-quote">"autodetect"</span>
    dynamicConfig=<span class="code-quote">"true"</span>&gt;
	
	&lt;defaultCache
		maxElementsInMemory=<span class="code-quote">"10"</span>
		eternal=<span class="code-quote">"false"</span>
		timeToIdleSeconds=<span class="code-quote">"5"</span>
		timeToLiveSeconds=<span class="code-quote">"10"</span>
		overflowToDisk=<span class="code-quote">"true"</span> /&gt;
	
    &lt;cache name=<span class="code-quote">"SimplePageCachingFilter"</span>
		maxElementsInMemory=<span class="code-quote">"100"</span>
		eternal=<span class="code-quote">"false"</span>
		overflowToDisk=<span class="code-quote">"false"</span>
		timeToIdleSeconds=<span class="code-quote">"5"</span>
		timeToLiveSeconds=<span class="code-quote">"10"</span>
        memoryStoreEvictionPolicy=<span class="code-quote">"LFU"</span> /&gt;
<span class="code-tag">&lt;/ehcache&gt;</span>
</pre>
</div></div>

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

<h1><a name="JAX-RSAdvancedFeatures-RESTfulserviceswithoutannotations"></a>RESTful services without annotations </h1>

<p>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.<br/>
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.</p>

<p>User model schema type is described in the <a href="http://svn.apache.org/repos/asf/cxf/trunk/rt/frontend/jaxrs/src/main/resources/schemas/jaxrs.xsd" class="external-link" rel="nofollow">jaxrs.xsd</a>. </p>

<p>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. <br/>
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.<br/>
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.</p>

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

<p>Here is an example :</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-xml">
<span class="code-tag">&lt;model xmlns=<span class="code-quote">"http://cxf.apache.org/jaxrs"</span>&gt;</span>
  &lt;resource name=<span class="code-quote">"org.apache.cxf.systest.jaxrs.BookStoreNoAnnotations"</span> path=<span class="code-quote">"bookstore"</span>
    produces=<span class="code-quote">"application/json"</span> consumes=<span class="code-quote">"application/json"</span>&gt;
    <span class="code-tag">&lt;operation name=<span class="code-quote">"getBook"</span> verb=<span class="code-quote">"GET"</span> path=<span class="code-quote">"/books/{id}"</span> produces=<span class="code-quote">"application/xml"</span>&gt;</span>
       <span class="code-tag">&lt;param name=<span class="code-quote">"id"</span> type=<span class="code-quote">"PATH"</span>/&gt;</span>
    <span class="code-tag">&lt;/operation&gt;</span>
    <span class="code-tag">&lt;operation name=<span class="code-quote">"getBookChapter"</span> path=<span class="code-quote">"/books/{id}/chapter"</span>&gt;</span>
       <span class="code-tag">&lt;param name=<span class="code-quote">"id"</span> type=<span class="code-quote">"PATH"</span>/&gt;</span>
    <span class="code-tag">&lt;/operation&gt;</span>
    <span class="code-tag">&lt;operation name=<span class="code-quote">"updateBook"</span> verb=<span class="code-quote">"PUT"</span>&gt;</span>
       <span class="code-tag">&lt;param name=<span class="code-quote">"book"</span> type=<span class="code-quote">"REQUEST_BODY"</span>/&gt;</span>
    <span class="code-tag">&lt;/operation&gt;</span>
  <span class="code-tag">&lt;/resource&gt;</span>
  <span class="code-tag">&lt;resource name=<span class="code-quote">"org.apache.cxf.systest.jaxrs.ChapterNoAnnotations"</span>&gt;</span>
    <span class="code-tag">&lt;operation name=<span class="code-quote">"getItself"</span> verb=<span class="code-quote">"GET"</span>/&gt;</span>
    <span class="code-tag">&lt;operation name=<span class="code-quote">"updateChapter"</span> verb=<span class="code-quote">"PUT"</span> consumes=<span class="code-quote">"application/xml"</span>&gt;</span>
        <span class="code-tag">&lt;param name=<span class="code-quote">"content"</span> type=<span class="code-quote">"REQUEST_BODY"</span>/&gt;</span>
    <span class="code-tag">&lt;/operation&gt;</span>
  <span class="code-tag">&lt;/resource&gt;</span>
<span class="code-tag">&lt;/model&gt;</span>
</pre>
</div></div>

<p>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.<br/>
The runtime will introspect the <a href="http://svn.apache.org/repos/asf/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStoreNoAnnotations.java" class="external-link" rel="nofollow">org.apache.cxf.systest.jaxrs.BookStoreNoAnnotations</a> class and check the return types for both 'getBook' and 'getBookChapter' methods.  BookStoreNoAnnotations.getBookChapter() method's return type is <a href="http://svn.apache.org/repos/asf/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/ChapterNoAnnotations.java" class="external-link" rel="nofollow">org.apache.cxf.systest.jaxrs.ChapterNoAnnotations</a> 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.  </p>

<p>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.</p>

<p>You can use a comma-seperated 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").</p>

<p>Please also see this <a href="http://svn.apache.org/repos/asf/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/resources/resources2.xml" class="external-link" rel="nofollow">model file</a> 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. </p>

<h2><a name="JAX-RSAdvancedFeatures-Configuration"></a>Configuration </h2>

<p>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. </p>

<p>Please see this <a href="http://svn.apache.org/repos/asf/cxf/trunk/systests/jaxrs/src/test/resources/jaxrs/WEB-INF/beans.xml" class="external-link" rel="nofollow">bean</a> Spring configuration file, look at jaxrs server beans with 'bookservice6' and 'bookservice7' names.</p>

<p>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.<br/>
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.</p>

<p>Please have a look at <a href="http://svn.apache.org/repos/asf/cxf/trunk/systests/jaxrs/src/test/resources/jaxrs_proxy/WEB-INF/beans.xml" class="external-link" rel="nofollow">this Spring bean</a>. 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.</p>


<p>You can also register a model programmatically, for example :</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
JAXRSServerFactoryBean sf = <span class="code-keyword">new</span> JAXRSServerFactoryBean();
            sf.setAddress(<span class="code-quote">"http:<span class="code-comment">//localhost:9080/"</span>);
</span><span class="code-object">String</span> modelRef = <span class="code-quote">"classpath:/org/apache/cxf/systest/jaxrs/resources/resources2.xml"</span>;
sf.setModelRef(modelRef);

<span class="code-comment">// or <span class="code-keyword">if</span> you have <span class="code-keyword">interface</span> classes described in the model already loaded, ex : OSGI
</span><span class="code-comment">// sf.setModelRefWithServiceClass(modelRef, BookStoreNoAnnotationsInterface.class);
</span>
<span class="code-comment">// register an actual bean only <span class="code-keyword">if</span> the model describes interfaces
</span>sf.setServiceBeans(<span class="code-keyword">new</span> BookStoreNoAnnotationsImpl());
</pre>
</div></div>

<p>Please also see <a href="http://svn.apache.org/repos/asf/cxf/trunk/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerUserResourceTest.java" class="external-link" rel="nofollow">this system test</a> for the example of how model beans like UserResource can be created and registered programmatically.</p>

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

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
JAXRSClientFactoryBean cf = <span class="code-keyword">new</span> JAXRSClientFactoryBean();
cf.setAddress(<span class="code-quote">"http:<span class="code-comment">//localhost:9080/"</span>);
</span><span class="code-object">String</span> modelRef = <span class="code-quote">"classpath:/org/apache/cxf/systest/jaxrs/resources/resources2.xml"</span>;
sf.setModelRef(modelRef);
BookStoreNoAnnotations proxy = cf.create(BookStoreNoAnnotations.class);
</pre>
</div></div>

<p>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 <a href="http://svn.apache.org/repos/asf/cxf/trunk/systests/jaxrs/src/test/resources/jaxrs_non_spring/WEB-INF/web.xml" class="external-link" rel="nofollow">web.xml</a>. See CXFServlet3 and CXFServlet4 servlet declarations. Note that CXFServlet4 registers a model containing interfaces so it also registers a BookStoreNoAnnotationsImpl service class.</p>

<p>The workaround is to create a custom servlet :</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">public</span> class JAXRSUserModelServlet <span class="code-keyword">extends</span> CXFNonSpringJaxrsServlet  {

@Override
<span class="code-keyword">public</span> void loadBus(ServletConfig servletConfig) <span class="code-keyword">throws</span> ServletException {

<span class="code-keyword">super</span>.loadBus(servletConfig);

JAXRSServerFactoryBean sf = <span class="code-keyword">new</span> JAXRSServerFactoryBean();
<span class="code-object">String</span> address = servletConfig.getInitParameter(SERVICE_ADDRESS_PARAM); <span class="code-comment">//jaxrs.address
</span><span class="code-keyword">if</span> (address == <span class="code-keyword">null</span>) {
address = <span class="code-quote">"/"</span>;
}
sf.setAddress(address);

<span class="code-comment">// modelRef needs to start from 'classpath:', ex 'classpath:/WEB-INF/models/model1.xml
</span><span class="code-object">String</span> modelRef = servletConfig.getInitParameter(<span class="code-quote">"user.model"</span>);
sf.setModelRef(modelRef);
sf.create();
}
</pre>
</div></div>
    </div>
        <div id="commentsSection" class="wiki-content pageSection">
        <div style="float: right;">
            <a href="https://cwiki.apache.org/confluence/users/viewnotifications.action" class="grey">Change Notification Preferences</a>
        </div>
        <a href="https://cwiki.apache.org/confluence/display/CXF20DOC/JAX-RS+Advanced+Features">View Online</a>
        |
        <a href="https://cwiki.apache.org/confluence/pages/diffpagesbyversion.action?pageId=24190907&revisedVersion=33&originalVersion=32">View Changes</a>
                |
        <a href="https://cwiki.apache.org/confluence/display/CXF20DOC/JAX-RS+Advanced+Features?showComments=true&amp;showCommentArea=true#addcomment">Add Comment</a>
            </div>
</div>
</div>
</div>
</div>
</body>
</html>

Mime
View raw message