Reputation: 7255
I am trying to create a rest api server on top of my quartz scheduler. I want to be able to return the org.quartz.Trigger and org.quartz.JobDetail objects as JSON. The problem is that I cannot add the @XmlRootElement to these classes without having to recompile the jar and this causes problems with future upgrades etc. I have tested and am able to serialize a list of classes when adding the @XmlRootElement but when I try to return a List I get the error "A message body writer for Java class java.util.ArrayList, and Java type java.util.List, and MIME media type application/json was not found". I have tried adding a custom MessageBodyWriter but that does not seem to fix the problem either. Is there a way to marshal the quartz classes to JSON without having to modify the quartz code to add the @XmlRootElement. I am using this in an embedded web server with jetty btw.
@Path("/jobs")
public class JobsResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
public List<Trigger> listScheduledJobs() throws SchedulerException {
return TaskEngine.getInstance().listScheduledJobs();
}
}
Web server configuration
public class TaskEngineWebServer {
private static final Logger logger = Logger.getLogger(TaskEngineWebServer.class.getName());
private Server server;
public TaskEngineWebServer() {
this(8585);
}
public TaskEngineWebServer(Integer port) {
server = new Server(port);
logger.info("Configuring rest service to start at url /r");
ServletContextHandler handler = new ServletContextHandler(ServletContextHandler.NO_SECURITY);
//handler.getInitParams().put("com.sun.jersey.api.json.POJOMappingFeature", "true");
PackagesResourceConfig packagesResourceConfig = new PackagesResourceConfig("com.hp.vf.scheduler.server.rest");
ServletContainer servletContainer = new ServletContainer(packagesResourceConfig);
handler.addServlet(new ServletHolder(servletContainer), "/r/*");
server.setHandler(handler);
logger.info("Done configuring rest service");
}
public void start() throws Exception {
server.start();
}
public void stop() throws Exception {
server.stop();
}
public boolean isStarted() {
return server.isStarted();
}
public boolean isStopped() {
return server.isStopped();
}
}
Upvotes: 0
Views: 2278
Reputation: 7255
I finally figured out a clean solution, it involves creating my own MediaBodyWriter class and adding it as a provider. You have to make sure you are not using the jersey-bundle jar as the default jaxb to json provider will override yours.
jars required
jersey-core jersey-servlet jersey-server
jackson-annotations jackson-databind jackson-core
I found this MediaWriter example on the web somewhere. Sorry for not having the url but thanks to whoever write it.
@Provider
@Produces({ MediaType.APPLICATION_JSON })
public class JacksonWriter implements MessageBodyWriter<Object> {
private static final ObjectMapper MAPPER = new ObjectMapper();
@Override
public boolean isWriteable(Class<?> aClass, Type type, Annotation[] annotations, MediaType mediaType) {
return true;
}
@Override
public long getSize(Object value, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return -1;
}
@Override
public void writeTo(Object value, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
OutputStream entityStream) {
try {
MAPPER.writeValue(entityStream, value);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}
When it loads you will see a log message that your provider was loaded.
This gave me the json output I was expecting as it does not rely on the JAXB annotations and simply uses the object mapper/ reflection. Probably less efficient but for my case it does not matter.
Upvotes: 1
Reputation: 2691
I dont think you can return a List as JSON directly. You need to have a wrapper class which contains this list. For eg try something like this
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class TriggerWrapper{
private List<Triggers> triggers;
public List<Triggers> getTriggers(){
if(triggers==null){
triggers = new ArrayList<Triggers>();
}
return triggers;
}
}
Then in your rest service class :
@Path("/jobs")
public class JobsResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
public TriggerWrapper listScheduledJobs() throws SchedulerException {
TriggerWrapper response = new TriggerWrapper();
List<Triggers> triggers = TaskEngine.getInstance().listScheduledJobs();
response.getTriggers.addAll(triggers);
return response;
}
}
Your json would something like this :
{
"triggerwrapper": {
"triggers": [
{
"triggerid": 1
},
{
"triggerid": 2
}
]
}
}
And ofcourse if you want you can drop the root element tag from your json its configurable in jersey.
Upvotes: 1