Luckly the Apache Camel cxfbean component makes putting together a REST web service much easier. Just annotate your service class and use it as the resource for the cxfbean component.
#!/usr/bin/env groovy
@Grab(group="org.apache.camel", module="camel-core", version="2.8.0")
@Grab(group="org.apache.camel", module="camel-jetty", version="2.8.0")
@Grab(group="org.apache.camel", module="camel-cxf", version="2.8.0")
@Grab(group='ch.qos.logback', module='logback-classic', version='0.9.29')
import org.apache.camel.CamelContext
import org.apache.camel.impl.DefaultCamelContext
import org.apache.camel.impl.SimpleRegistry
import org.apache.camel.builder.RouteBuilder
import javax.ws.rs.*
@Path("/example")
class MyExampleResource{
@GET
@Produces("text/plain")
@Path("hello")
public String hello(){
"Hello World!"
}
}
CamelContext cxt = new DefaultCamelContext()
cxt.registry = new SimpleRegistry()
cxt.registry.registry.put("myResources", [new MyExampleResource()])
cxt.addRoutes(new RouteBuilder(){
void configure(){
from("jetty:http://localhost:8080?matchOnUriPrefix=true")
.to("cxfbean:myResources")
}
})
cxt.start()
If you want to produce and consume a data format like JSON, you simply pass the necessary processor to the list of cxfbean processors. The CXF libaries include a JSON processor that you can quickly hook up - but I ran into a couple of issues. First you have to annotate your data classes with JAXB annotations (which you may not be able to do depending on your situation.)
#!/usr/bin/env groovy
@Grab(group="org.apache.camel", module="camel-core", version="2.8.0")
@Grab(group="org.apache.camel", module="camel-jetty", version="2.8.0")
@Grab(group="org.apache.camel", module="camel-cxf", version="2.8.0")
@Grab(group='ch.qos.logback', module='logback-classic', version='0.9.29')
@Grab(group='com.google.code.gson', module='gson', version='1.7.1')
import org.apache.camel.CamelContext
import org.apache.camel.impl.DefaultCamelContext
import org.apache.camel.impl.SimpleRegistry
import org.apache.camel.builder.RouteBuilder
import javax.ws.rs.*
import javax.ws.rs.ext.MessageBodyWriter
import javax.ws.rs.ext.MessageBodyReader
import java.lang.reflect.Type
import java.lang.annotation.Annotation
import javax.ws.rs.core.MediaType
import javax.ws.rs.core.MultivaluedMap
import com.google.gson.Gson
class Person {
Integer id
String name
public String toString(){
return "$id $name"
}
}
@Path("/example")
class MyExampleResource{
@GET
@Produces("text/plain")
@Path("hello")
public String hello(){
"Hello World!"
}
@GET
@Produces("application/json")
@Path("person/{id}")
public Person getPerson(@PathParam("id") Integer id){
Person p = new Person()
p.id = id
p.name = "Bob"
return p
}
@PUT
@Consumes("application/json")
@Produces("application/json")
@Path("person")
public Person addPerson(Person p){
println "Received ${p.toString()}"
p.id = 123
return p
}
}
class GsonProvider implements
MessageBodyWriter<Object>,
MessageBodyReader<Object>{
Gson gson = new Gson()
boolean isWriteable(Class<?> aClass, Type type,
Annotation[] annotations, MediaType mediaType){
mediaType.subtype == 'json'
}
long getSize(Object t, Class<?> aClass, Type type,
Annotation[] annotations, MediaType mediaType){
0
}
void writeTo(Object t, Class<?> aClass, Type type,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> stringObjectMultivaluedMap,
OutputStream outputStream)
throws java.io.IOException, javax.ws.rs.WebApplicationException{
outputStream.write(gson.toJson(t).bytes)
}
Object readFrom(Class<Object> tClass, Type type,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, String> stringStringMultivaluedMap,
InputStream inputStream) {
gson.fromJson(inputStream.text, tClass)
}
boolean isReadable(Class<?> aClass, Type type,
Annotation[] annotations, MediaType mediaType) {
mediaType.subtype == 'json'
}
}
CamelContext cxt = new DefaultCamelContext()
cxt.registry = new SimpleRegistry()
cxt.registry.registry.put("myResources", [new MyExampleResource()])
cxt.registry.registry.put("jsonProvider", new GsonProvider())
cxt.addRoutes(new RouteBuilder(){
void configure(){
from("jetty:http://localhost:8080?matchOnUriPrefix=true")
.to("cxfbean:myResources?providers=#jsonProvider")
}
})
cxt.start()
Since originally writing this I discovered how to get the JSON parson that comes with CXF to work with Groovy classes. Include the XmlAccessorType annotation telling it to use FIELD.
...
import javax.xml.bind.annotation.XmlRootElement
import javax.xml.bind.annotation.XmlAccessType
import javax.xml.bind.annotation.XmlAccessorType
import org.apache.cxf.jaxrs.provider.JSONProvider
...
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
class Person {
Integer id
String name
public String toString(){
return "$id $name"
}
}
...
cxt.registry.registry.put("jsonProvider", new JSONProvider())
...