Objects as parameters in Apache CXF REST service method - java

What Exactly i need to do is to have a web service method in a REST API written in Apache CXF to accept a request like the following (preferably specifying a custom object as a parameter type)
{
"action":"read",
"resource:"new resource"
}
For now my method can do the same but it would expect a JSON string as the request body. But I need the service to describe the request parameters to the client. Meaning that in the wadl definition it should show the exact parameters that should be sent from the client. The ideal definition would be something similar to
<resource path="by-attrib">
<method name="GET">
<request>
<param name="Accept" style="header" type="xs:string"/>
<param name="Auth_Type" style="header" type="xs:string"/>
<param name="Authorization" style="header" type="xs:string"/>
<representation mediaType="application/json">
<param name="action" style="plain" type="xs:string"/>
<param name="resource" style="plain" type="xs:string"/>
</representation>
</request>
<response>
<representation mediaType="application/json">
<param name="result" style="plain" type="xs:string"/>
</representation>
</response>
</method>
</resource>
Is this possible with CXF?
Please note that using #FormParam is not what I need, if I use form params, I get issues in sending a request using XML to the same method
Thank You

Example with CXF and jackson
The service interface (use POST, not GET)
#POST
#Path("/yourservice")
#Consumes({ MediaType.APPLICATION_JSON})
#Produces({
MediaType.APPLICATION_JSON,
MediaType.APPLICATION_XML})
public Result postYourService(YourData data) throws WebApplicationException;
The service impl (nothing special)
public Result postYourService(YourData data){
Result r = new Result();
r.setResult("result");
return r;
}
The data objects (Using jaxb to simplify encoding/decoding of json or xml)
#XmlRootElement(name = "YourData")
public class YourData {
private String action;
private String resource;
//getter & setters
}
#XmlRootElement(name = "Result")
public class Result {
private String result;
//getter & setters
}
The spring configuration of CXF server and jackson. Jackson provider class depends on which version of CXF are you using. Therefore if the JacksonJaxbJsonProvider is not on your cxf package, look at the documentation
<jaxrs:server id="yourServiceREST" address="/services">
<jaxrs:serviceBeans>
<ref bean="yourService" />
</jaxrs:serviceBeans>
<jaxrs:providers>
<!--<ref bean="authenticationFilter" />-->
<!--<ref bean="exceptionMapper" />-->
<!-- <ref bean="corsFilter" /> -->
<ref bean="jackson" />
</jaxrs:providers>
</jaxrs:server>
<bean id="yourService" class="YourServiceImpl">
</bean>
<bean id="jackson" class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider" />
Try to deploy and invoke to
POST /services/yourservice
{ "action":"read", "resource:"new resource"}
I do not know if WADL is going to be well generated because sometimes CXF fails. Be lucky!

actually I found the answer
It is to use bean injection
described in their documentation itself, sorry for not RTFM
http://cxf.apache.org/docs/jax-rs-basics.html
under Parameter Beans
Basic idea is to use a java Bean (zero argument constructor with setters and getters) and add it to the web service parameters
You however need to specify one of #QueryParam #FormParam #PathParam for it to work

Related

Camel CXF - Security (Authentication)

I created a webservice with apache camel using CXF component as bellow :
blueprint.xml:
<bean class="ngtrend.ws.Testws"/>
<!-- Defined the server endpoint to create the cxf-rs consumer -->
<cxf:rsServer id="rsServer" address="http://localhost:9050/route"
serviceClass="ngtrend.ws.Testws" />
<camelContext xmlns="http://camel.apache.org/schema/blueprint">
<route id="timerToLog">
<from uri="cxfrs://bean://rsServer"/>
<to uri="bean:ngtrend.ws.HelloBean?method=test(Exchange)"/>
<log message="${body}"/>
</route>
</camelContext>
Testws.java:
public class Testws {
#GET
#Path("/test/{id}")
#Produces("application/xml")
//#Consumes("text/xml")
public Integer getAssets(#PathParam("id") int id){
return null;
}
}
and I would like to secure it forcing the customer to send ( or enter on a dialog box if using a browser) login and password (BASIC Http authentication). How can i make this configuration ?
In CXF framework, restful services authentication can be done by using the following approach:
<cxf:rsServer id="rsServer"
address="http://localhost:9050/route">
<jaxrs:serviceBeans>
<ref bean="serviceBean"/>
</jaxrs:serviceBeans>
<jaxrs:providers>
<ref bean="authenticationHandler"/>
</jaxrs:providers>
</cxf:server>
<bean id="serviceBean" class="ngtrend.ws.Testws"/>
<bean id="authenticationHandler" class="yourpackage.Class" />
Create your own handler for authenticationHandler that will implement import org.apache.cxf.jaxrs.ext.RequestHandler.
Use the authentication strategy needed in this class , for example authenticate against database etc.. This should allow for basic authentication.
You can write a class which implements ContainerRequestFilter. And then set it in the cxf:providers as below:
<bean id="authenticationHandler" class="a class which implements ContainerRequestFilter" />
<cxf:rsServer id="xxxRsServer"
address="/xxxservice" serviceClass="xxx.XXXService"
loggingFeatureEnabled="true" loggingSizeLimit="20">
<cxf:providers>
<ref component-id="authenticationHandler"/>
</cxf:providers>
</cxf:rsServer>
In this way, you could override below method
public void filter(ContainerRequestContext requestContext)
For example, you could make a simple authentication based on requestContext.getHeaderString("UserPassInfo"). If succeed, do nothing, otherwise call requestContext.abortWith(Response.status(401).header("WWW-Authenticate", "Basic").build());

CXF/JAXB fails at marshalling primitive long return value

I have a simple CXF method that returns a long
#GET
#Path("/count/{foo}/{bar}")
long count(#PathParam("foo") String foo, #PathParam("bar") String bar)
I have a CXF server with JAXB setup for it
<jaxrs:server id="myServer" address="/">
<jaxrs:providers>
<bean id="jaxbProvider" class="org.apache.cxf.jaxrs.provider.JAXBElementProvider">
<property name="singleJaxbContext" value="true" />
<property name="skipJaxbChecks" value="true" />
<property name="validateOutput" value="false" />
</bean>
</jaxrs:providers>
<jaxrs:serviceBeans>
<ref bean="myServiceImpl" />
</jaxrs:serviceBeans>
</jaxrs:server>
But when I call this method, the server fails at marshalling the result :
Caused by: com.sun.istack.SAXException2: unable to marshal type
"java.lang.Long" as an element because it is missing an
#XmlRootElement annotation
How come CXF is not able to marshal a long ?
Thanks for your help
EDIT
I am actually writing a mockup implementation of an existing service (for test purposes).
I have no control on the API of the existing service.
And its current implementation returns something like
Response-Code: 200
Encoding: ISO-8859-1
Content-Type: application/xml
Headers: {connection=[close], Content-Length=[3], content-type=[application/xml], Date=[Wed, 23 Jul 2014 08:00:31 GMT]}
Payload: 121
Which is no real XML i agree.
But the current client does not complain either, and does no contain any magic for it.
Why do you need to create xml structure for a single return value, You retrun it as text and add Annotation Produces with return type content as plain/text.
#GET
#Produces("text/plain")
#Path("/count/{foo}/{bar}")
long count(#PathParam("foo") String foo, #PathParam("bar") String bar)
If you are using cxf client you can read it as long as follows
Response response = client.get();
response.readEntity(Long.class);
EDIT
Remove the jaxrs:providers, cxf internally handles jaxb conversions by default
<jaxrs:server id="myServer" address="/">
<jaxrs:serviceBeans>
<ref bean="myServiceImpl" />
</jaxrs:serviceBeans>
</jaxrs:server>
Here is the below code I tested, however when I added the provider it gives error.
#Path("/add")
#Produces("application/xml")
#GET
public Long add(#QueryParam("v1")int v1, #QueryParam("v2")int v2){
long result =v1+v2;
return result;
}
#Path("/subtract")
#Produces("application/xml")
#GET
public Output subtract(#QueryParam("v1")int v1, #QueryParam("v2")int v2){
Output out = new Output();
out.setResult(v1-v2);
return out;
}

Migrating to CXF with Jaxb from xfire with agesi

I'm migrating my xfire soap project which uses aegis for databinding to cxf with jaxb. I got the new cxf project working for old xfire requests with aegis binding. But when i move the databinding to jaxb unmarshalling errror occurs.
This is my cxf web service definition.
<!--<bean id="aegisBean" class="org.apache.cxf.aegis.databinding.AegisDatabinding" scope="prototype"/> -->
<bean id="jaxbBean" class="org.apache.cxf.jaxb.JAXBDataBinding" scope="prototype"/>
<bean id="jaxws-and-aegis-service-factory" class="org.apache.cxf.jaxws.support.JaxWsServiceFactoryBean"
scope="prototype">
<property name="dataBinding" ref="jaxbBean"/>
<property name="serviceConfigurations">
<list>
<bean class="org.apache.cxf.jaxws.support.JaxWsServiceConfiguration"/>
<bean class="org.apache.cxf.aegis.databinding.XFireCompatibilityServiceConfiguration"/>
<bean class="org.apache.cxf.service.factory.DefaultServiceConfiguration"/>
</list>
</property>
</bean>
<jaxws:endpoint id="trace" address="/trace" implementor="#traceImplBean">
<jaxws:serviceFactory>
<ref bean="jaxws-and-aegis-service-factory"/>
</jaxws:serviceFactory>
<jaxws:inInterceptors>
<bean class="org.apache.cxf.interceptor.LoggingInInterceptor"/>
</jaxws:inInterceptors>
<jaxws:outInterceptors>
<bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"/>
</jaxws:outInterceptors>
</jaxws:endpoint>
I used #XMLRootElement Annotaion on my DTOs as following.
#XmlRootElement(name = "context" )
public class Context implements Serializable {
private KeyValues keyValues;
.....
}
#XmlRootElement(name = "keyValues" )
public class KeyValues implements Serializable {
private String tag;
private String value;
....
}
One method which i tested generated following soap request for cxf
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:pay="http://example.project.com">
<soapenv:Header/>
<soapenv:Body>
<pay:trace>
<pay:context>
<keyValues>
<tag>tag</tag>
<value>value</value>
</keyValues>
</pay:context>
</pay:trace>
</soapenv:Body>
</soapenv:Envelope>
HOWEVER old xfire generate following request, I have mark the difference.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:pay="http://example.project.com" xmlns:api="http://example.com">
<soapenv:Header/>
<soapenv:Body>
<pay:trace>
<pay:context>
<api:keyValues>
***<api:KeyValues>***
<api:tag>tag</api:tag>
<api:value>value</api:value>
***</api:KeyValues>***
</api:keyValues>
</pay:context>
</pay:trace>
</soapenv:Body>
</soapenv:Envelope>
I got following exception when i tried to send xfire request to cxf service.
javax.xml.bind.UnmarshalException: unexpected element (uri:"http://example.project.com", local:"keyValues"). Expected elements are <{}keyValues>
So I think i need to add additional tags to cxf request inorder to compatible with xfire. Does anyone knows how to resolve this ?
Thanks in advance.
JAXB, by default, uses unqualified elements whereas Aegis/XFire by default used qualified elements. Couple ways around that:
1) For every element, specify the namespace.
#XmlElement(name = "tag", namespace = "http:...")
likely easier:
2) Add a package-info.java with:
#javax.xml.bind.annotation.XmlSchema(namespace = "http://......",
elementFormDefault = XmlNsForm.QUALIFIED)

Mule - Set properties on spring object call in Mule flow

I'm using Mule 3.3.CE
I have a Class called SpringObject which implements Callable interface
package com.threads.test;
import org.mule.api.MuleEventContext;
import org.mule.api.lifecycle.Callable;
public class SpringObject implements Callable {
private String someData;
public String getSomeData() {
return someData;
}
public void setSomeData(String someData) {
this.someData = someData;
}
#Override
public Object onCall(MuleEventContext eventContext) throws Exception {
System.out.println("CALL SPRING --->>"+someData);
return eventContext.getMessage();
}
}
And my flow is
<http:connector name="httpConnectorEntryPoint" doc:name="HTTP\HTTPS"/>
<spring:beans xmlns="http://www.springframework.org/schema/beans">
<spring:bean id="component" name="component" class="com.threads.test.SpringObject" lazy-init="false">
</spring:bean>
</spring:beans>
<flow name="TestThreadsFlow1" doc:name="TestThreadsFlow1">
<http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="8099" path="m" connector-ref="httpConnectorEntryPoint" doc:name="HTTP"/>
<set-payload value="#["ExitA"]" doc:name="Set Payload"/>
<component doc:name="Java">
<spring-object bean="component">
<property key="someData" value="Information"/>
</spring-object>
</component>
</flow>
The problem is that when I run my flow and use the http connector, the console shows
CALL SPRING --->>null
instead of
CALL SPRING --->>Information
What could be?
you can try to configure your spring bean outside the flow as follow:
<spring:bean id="component" name="component"
class="com.threads.test.SpringObject" lazy-init="false">
<spring:property name="someData" value="Information" />
</spring:bean>
and inside the flow do:
<component>
<spring-object bean="component" />
</component>
From the property element description in the XSD:
Sets a Mule property. This is a name/value pair that can be set on
components, services, etc., and which provide a generic way of
configuring the system. Typically, you shouldn't need to use a generic
property like this, since almost all functionality is exposed via
dedicated elements. However, it can be useful in configuring obscure
or overlooked options and in configuring transports from the generic
endpoint elements.
This means it's not intended for what you are trying to use it. The appropriate way to set a property in your bean is as follows:
<spring:bean id="component" name="component" class="com.threads.test.SpringObject" lazy-init="false">
<spring:property name="someData" value="Information"/>
</spring:bean>

Jersey Path Regex Causing Method Not Allowed for PUT

I'm testing out Jersey and I can't seem to figure out why I get a 405 Method Not Allowed when I call PUT:
#Singleton #Path("images")
public class ImageResource {
private static final String IMAGE_ID_PATH_PARAM = "{imageId : [A-Za-z0-9_\\-]+}";
private static final String EXTENSION_PATH_PARAM = "{extension : (\\.[A-Za-z]+)?}";
#GET #Path(IMAGE_ID_PATH_PARAM + EXTENSION_PATH_PARAM)
#Produces("image/*")
public Response getImage(#PathParam("imageId") String imageId,
#PathParam("extension") String extension, #QueryParam("mods") String mods) {
...
}
#PUT #Path(IMAGE_ID_PATH_PARAM)
#Consumes("image/*")
public Response putImage(#PathParam("imageId") String imageId, File image) {
...
}
}
PUT only works if I set the #GET path to #Path(IMAGE_ID_PATH_PARAM). When I add the extension part, I am getting a 405 status code. GET seems to work in both cases. Here's the output from a failed PUT:
$ curl -v --header "Content-Type: image/jpeg" --upload-file /Users/andy/Desktop/test.jpg http://localhost:9090/images/abcde
* About to connect() to localhost port 9090 (#0)
* Trying 127.0.0.1... connected
* Connected to localhost (127.0.0.1) port 9090 (#0)
> PUT /images/abcde HTTP/1.1
> User-Agent: curl/7.21.4 (universal-apple-darwin11.0) libcurl/7.21.4 OpenSSL/0.9.8r zlib/1.2.5
> Host: localhost:9090
> Accept: */*
> Content-Type: image/jpeg
> Content-Length: 48198
> Expect: 100-continue
>
< HTTP/1.1 100 Continue
< HTTP/1.1 405 Method Not Allowed
< Content-Type: text/html; charset=iso-8859-1
< Date: Mon, 20 Aug 2012 18:35:59 GMT
< Allow: GET,OPTIONS,HEAD
< Transfer-Encoding: chunked
I've also tried testing without the #Produces and #Consumes annotations and it did not work either.
Let's take a look at what happens when you send the request.
Situation 1: no extension
Your methods look like this:
#GET #Path(IMAGE_ID_PATH_PARAM)
#Produces("image/*")
public Response getImage(#PathParam("imageId") String imageId,
#PathParam("extension") String extension, #QueryParam("mods") String mods) {
...
}
#PUT #Path(IMAGE_ID_PATH_PARAM)
#Consumes("image/*")
public Response putImage(#PathParam("imageId") String imageId, File image) {
...
}
When you send the following request:
PUT http://localhost:9090/images/abcde
first of all, Jersey looks for resources with the URI in question:
http://localhost:9090/images/abcde
After the resource is found, it checks what methods can be used to access it.
In this case, you have a single resource with the path defined by IMAGE_ID_PATH_PARAM. This resource can be accessed by either GET or PUT requests. Just like you specified with the annotations.
Situation 2: extension added to getImage
You methods now look like this:
#GET #Path(IMAGE_ID_PATH_PARAM + EXTENSION_PATH_PARAM)
#Produces("image/*")
public Response getImage(#PathParam("imageId") String imageId,
#PathParam("extension") String extension, #QueryParam("mods") String mods) {
...
}
#PUT #Path(IMAGE_ID_PATH_PARAM)
#Consumes("image/*")
public Response putImage(#PathParam("imageId") String imageId, File image) {
...
}
Again, you send the same request:
PUT http://localhost:9090/images/abcde
And yet again, Jersey finds the first resource matching the URL. The resource is represented by your getImage method, just like the first time. This time again, the #GET annotation does not match your request. And just like before, Jersey tries to find another method available for the resource, in order to match your request.
This time, however, no such method is found so it returns a 405.
The reason why this happens is that the methods getImage and putImage now represent different resources. If you look carefully, the paths can be read like this (I'll omit the regex for clarity):
#Path({imageId}{extension}) for getImage
and
#Path({imageId}) for putImage
While these two paths, considering the regex, can become the same thing, Jersey still sees them as identifiers of separate resources.
If you take a look at the WADL (feel free to look here if you're not familiar with the standard) file generated by Jersey (it should be available at http://localhost:9090/application.wadl), you'll notice that this is exactly what's happening.
<application xmlns="http://research.sun.com/wadl/2006/10">
<resources base="http://localhost:9090/">
<resource path="images">
<resource path="{imageId : [A-Za-z0-9_\-]+}">
<param xmlns:xs="http://www.w3.org/2001/XMLSchema" name="imageId" style="template" type="xs:string"/>
<method id="putImage" name="PUT">
<request>
<representation mediaType="image/*"/>
</request>
<response>
<representation mediaType="*/*"/>
</response>
</method>
</resource>
<resource path="{imageId : [A-Za-z0-9_\-]+}{extension : (\.[A-Za-z]+)?}">
<param xmlns:xs="http://www.w3.org/2001/XMLSchema" name="extension" style="template" type="xs:string"/>
<param xmlns:xs="http://www.w3.org/2001/XMLSchema" name="imageId" style="template" type="xs:string"/>
<method id="getImage" name="GET">
<request>
<param xmlns:xs="http://www.w3.org/2001/XMLSchema" name="mods" style="query" type="xs:string"/>
</request>
<response>
<representation mediaType="image/*"/>
</response>
</method>
</resource>
</resource>
</resources>
</application>
Notice how images has two, separate sub-resources, each one with a single method.
Solution
Adding the EXTENSION_PATH_PARAM segment to putImage's #Path annotation causes these two methods to be mapped to a single resource again, so the problem disappears. Since the regex makes this part optional, you are free to omit it and pretend it doesn't exist.
The difference can be clearly seen in the generated WADL.
<application xmlns="http://research.sun.com/wadl/2006/10">
<resources base="http://localhost:9090/">
<resource path="images">
<resource path="{imageId : [A-Za-z0-9_\-]+}{extension : (\.[A-Za-z]+)?}">
<param xmlns:xs="http://www.w3.org/2001/XMLSchema" name="extension" style="template" type="xs:string"/>
<param xmlns:xs="http://www.w3.org/2001/XMLSchema" name="imageId" style="template" type="xs:string"/>
<method id="putImage" name="PUT">
<request>
<representation mediaType="image/*"/>
</request>
<response>
<representation mediaType="*/*"/>
</response>
</method>
<method id="getImage" name="GET">
<request>
<param xmlns:xs="http://www.w3.org/2001/XMLSchema" name="mods" style="query" type="xs:string"/>
</request>
<response>
<representation mediaType="image/*"/>
</response>
</method>
</resource>
</resource>
</resources>
</application>
In this case, images has exactly one sub-resource, which in turn has two available methods.
Personally, I find the automatic generation of WADL a fantastic feature of Jersey. It's a great way to see what's happening with your resource methods without spending too much time with curl or other REST client.

Categories

Resources