I am developing a Client-Server app with JAX-RS / Apache CXF, JSON
I would like Apache CXF to handle my exception transparently on both ends : Which means transforming the exception into a bean, serializing it with my Jackson Serializer (JSON) and then doing the over way around on client side.
I have seen several confusing posts/answers on this subject and came up with using the #WebFault annotation :
#WebFault(name=CODE, faultBean="foo.bar.FaultBean")
public class DuplicateRuleNameFault extends Exception {
static final public String CODE = "DUPLICATE_RULE_NAME";
private FaultBean faultBean;
public DuplicateRuleNameFault(String msg) {
super(msg);
this.faultBean = new FaultBean(msg);
}
public DuplicateRuleNameFault() {
}
public FaultBean getFaultBean() {
return faultBean;
}
public void setFaultBean(FaultBean faultBean) {
this.faultBean = faultBean;
}
}
With no success ... Currently, CXF seems to happily ignore the annotation on the Exception and handle it as an unknown exception : 500 status error and no response body generated on the server side.
Is there something specific I have to configure in the "" server element of my Spring context ? I already have Spring scanning my Exception/FaultBean classes (is it even needed BTW ?).
I would appreciate if you could point me at some working example.
Thanks.
#WebFault's are not part of the JAX-RS specification. You will want to read up on section 3.3.4 of the specification, which describes the different ways you can accomplish what you are trying to do.
Option 1
Design your resource classes to throw WebApplicationException's. Set the response property of these exceptions to be a valid JAX-RS response containing the fault beans you want to send to the client.
Option 2
Define exception mapping providers. You can create a hierarchy of these to handle all the common exceptions your application will throw. Or you can create a top level exception with an embedded bean and an exception handler for it. And then derive several specific exceptions from the top level one.
public abstract class MyApplicationException<T> extends Exception {
private T faultBean;
// Constructors, setters/getters
}
#Provider
public class MyApplicationExceptionHandler implements ExceptionMapper<MyApplicationException<?>> {
// Implementation
}
One way of doing this is by using the javax.ws.rs.core.Response object like so :
#GET
#Path("/")
public Response getBlah()
{
try {
return Response.status(Response.Status.OK)
.entity(<Object you want to return>).build();
}
catch (final DuplicateRuleNameFault e) {
return Response.status(Response.Status.BAD_REQUEST)
.entity(e.getFaultBean().getMsg()).build();
}
}
Related
I'd like to implement a simple REST-API with Jersey and grizzly. After beeing stuck in dependency-hell for a while, I ended up with an exception and I have no idea how to handle/interpret:
java.lang.IllegalArgumentException: The implementation class org.glassfish.jersey.inject.hk2.RequestContext must be in the Singleton scope
at org.jvnet.hk2.internal.ServiceLocatorImpl.checkConfiguration(ServiceLocatorImpl.java:1713)
at org.jvnet.hk2.internal.ServiceLocatorImpl.addConfiguration(ServiceLocatorImpl.java:2108)
at org.jvnet.hk2.internal.DynamicConfigurationImpl.commit(DynamicConfigurationImpl.java:262)
at org.glassfish.hk2.utilities.ServiceLocatorUtilities.bind(ServiceLocatorUtilities.java:166)
at org.glassfish.jersey.inject.hk2.AbstractHk2InjectionManager.<init>(AbstractHk2InjectionManager.java:65)
at org.glassfish.jersey.inject.hk2.ImmediateHk2InjectionManager.<init>(ImmediateHk2InjectionManager.java:38)
at org.glassfish.jersey.inject.hk2.Hk2InjectionManagerFactory$Hk2InjectionManagerStrategy$1.createInjectionManager(Hk2InjectionManagerFactory.java:55)
at org.glassfish.jersey.inject.hk2.Hk2InjectionManagerFactory.create(Hk2InjectionManagerFactory.java:73)
at org.glassfish.jersey.internal.inject.Injections.createInjectionManager(Injections.java:69)
at org.glassfish.jersey.server.ApplicationHandler.<init>(ApplicationHandler.java:259)
at org.glassfish.jersey.server.ApplicationHandler.<init>(ApplicationHandler.java:246)
at org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainer.<init>(GrizzlyHttpContainer.java:310)
at org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory.createHttpServer(GrizzlyHttpServerFactory.java:93)
at src.main.java.myApplication.main(myApplication.java:141)
The exception happens in my main-class on creating the http-server:
URI uri= URI.create("http://localhost:8080");
ResourceConfig config = new ResourceConfig();
config.register(myAPI.class);
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(uri, config);
try{
server.start();
} catch( IOException e ) {
logger.error("Exception on starting HTTP-Server.", e);
}
The myAPI.java is nothing special yet and also not important for the problem here, I think. Nevertheless, it looks like this:
public class myAPI extends ResourceConfig{
private static final ObjectMapper mapper = new ObjectMapper();
public myAPI(){
}
#Path("/hello")
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response foo() {
ObjectNode json = mapper.createObjectNode();
json.put("status", "ok");
return Response.status(Response.Status.OK).entity(json).build();
}
}
My classpath contains the following huge amount of libraries:
commons-logging-1.1.3.jar
grizzly-framework-3.0.0.jar
grizzly-http-server-3.0.0.jar
grizzly-http-server-multipart-3.0.0.jar
hk2-api-2.6.1.jar
hk2-locator-2.6.1.jar
hk2-utils-2.6.1.jar
jakarta.annotation-api-2.0.0.jar
jakarta.inject-api-2.0.0.jar
jakarta.ws.rs-api-3.0.0.jar
javax.annotation-api-1.3.2.jar
javax.inject-1.jar
jersey-common-3.0.2.jar
jersey-container-grizzly2-http-3.0.2.jar
jersey-guava-2.22.1.jar
jersey-hk2-3.0.2.jar
jersey-server-3.0.2.jar
org.apache.commons.logging-1.1.3.jar
jackson-annotations-2.9.0.jar
jackson-core-2.9.4.jar
jackson-databind-2.9.4.jar
-
httpclient-4.5.13.jar
httpcore-4.4.14.jar
httpmime-4.5.13.jar
log4j-api-2.14.1.jar
log4j-core-2.14.1.jar
The libraries below the dash are needed for another module (http-client) inside my application. I know that I probably could have used some of the libraries there, which I also use for the http-server, but I couldn't get it to work otherwise. But that's not the focus here, only when this is responsible for some kind of collusions due to multiple implementations?! After the long trail-and-error in dependency-hell, it is also possible that some libraries are even not needed.
So the core-question is, how I can put the RequestContext into the singleton-scope?! I haven't even declared a RequestContext anywhere. Do I have to add it in some way?
Side-fact: I also tried the same with the Jersey-JdkHttpServerFactory and ended up with the same error.
I am experiencing a strange issue in our javax based rest web services. The api generates a json response by serializing a POJO class annotated using jaxb annotations. The rest api's are deployed on glassfish application server. The issue is that the api response is not consistent and changes on application restart. On making the api call, the json sometimes respects the jaxb annotations and sometimes the response is based on the getter/setter methods.
The pojo class is as below
#XmlRootElement(name = "records")
#XmlType(propOrder = {"number_of_records", "record"})
public class RecordsOutputXml {
private int recordNum;
private int numberOfRecords;
public RecordsOutputXml() {
//....
}
#XmlElement(name = "record")
public int getRecordNum() {
return records;
}
public void setRecords(List<RecordOutput> records) {
this.records = records;
}
#XmlElement(name = "number_of_records")
public int getNumber_of_records() {
return numberOfRecords;
}
public void setNumberOfRecords(int numberOfRecords) {
this.numberOfRecords = numberOfRecords;
}
}
The two different responses are as below
Respecting the xml annotations
{
"number_of_records": 1,
"record": 101
}
Fetching it from method names
{
"recordNum": 101,
"number_of_records": 1,
}
On troubleshooting we understood this is because the order in which the implementations of MessageBodyWriter classes are loaded. On debugging we can see two implementations:
Custom implementation that extends JacksonJsonProvider and has #Provider annotation
org.glassfish.jersey.jackson.JacksonFeature enabled through "jersey.config.server.provider.classnames" init-param in the web.xml.
Depending on the order of these classes in the list (_getMessageBodyReader() method in MessageBodyFactory. The comparator doesn't seem to have any impact) the corresponding class is called. #1 does not respect javax.xml.bind.annotation while #2 registers JacksonJaxbJsonProvider that interprets them.
I have a few questions
Is the above analysis correct?
Are #Provider & "jersey.config.server.provider.classnames" param in web.xml two ways to provide custom MessageBodyWriter implementations? Could they both be used simultaneously?
What are the possible solutions to resolve this?
I have a Jersey endpoint which uses a custom OSGi Service ExceptionManager Service.
#Path("service")
public class ServiceFacade {
private volatile ExceptionManager exceptionManager;
public ServiceFacade() {
BundleContext bC = FrameworkUtil.getBundle(ServiceFacade.class).getBundleContext();
ServiceReference<ExceptionManager> sR = bC.getServiceReference(ExceptionManager.class);
if (sR != null)
this.exceptionManager = bC.getService(sR);
}
#GET
#Consumes({MediaType.APPLICATION_JSON})
#Produces(MediaType.APPLICATION_JSON)
public Response sayHello() {
try {
if (exceptionManager == null)
return Response.status(Status.SERVICE_UNAVAILABLE).build();
// Do some work...
} catch (Exception e) {
exceptionManager.handle(e);
}
}
}
This Jersey class is added to the Jersey Application as a simple class, that means that every time a user hits this endpoint, a new instance of this class is created to handle the request. As you can see, the class contains a constructor which initializes the ExceptionManager Service. My question is, isn't there a simplified way of retrieving the service without going to BundleContext?
I have seen DependencyManager, but this bundle seems to only add the dependencies to the class (ServiceFacade in this case) during the Activation process, but that dependency resolution is too early this has to be done during run-time, every time an instance is created. Bellow is an approximation with DependencyManager but is not a solution for this:
public class Activator extends DependencyActivatorBase {
#Override
public void init(BundleContext bundleContext, DependencyManager dependencyManager) throws Exception {
dependencyManager.add(createComponent()
.setImplementation(ServiceFacade.class)
.add(createServiceDependency()
.setService(ExceptionManager.class)
.setRequired(true));
}
}
Thanks.-
You can obtain the reference to an OSGi service without accessing to BundleContext by using Declarative Services. A tutorial can be found here.
You can make the endpoint a singleton resource. This way you can let the dependency manager create a single instance and inject services and then add that instance to the Jersey application.
There are a few limitations, like Jersey's field or constructor injection does not work. You also have to be careful about concurrency when using fields of the resource.
I want my ExceptionMapper to catch all exceptions and log them... I'm using Jersey 2.0 right now.
I have an exception mapper like so:
#Provider
public class RestExceptionMapper implements ExceptionMapper<Exception> {
#Override
public Response toResponse(Exception e) {
log(e);
if (e instanceof WebApplicationException) {
return ((WebApplicationException) e).getResponse();
} else {
return buildResponse(e);
}
This exception mapper only gets called for non-WebApplication application exceptions.
How do I make one global exception mapper catch all the exceptions so I can log them. Is there another way I should approach this?
Thanks
Judging from the source code of Jersey where this is handled there is no way to do this with an ExceptionMapper. WebApplicationExceptions get a special treatment are never mapped.
A way to log all exceptions is to set the logger org.glassfish.jersey.server.ServerRuntime to FINER.
I'm using Jersey 1.17 on the server side to process REST requests and JAXB 2 to unmarshall the XML request content.
Context
This is the Jersey method I use. The MyDTO class uses the #XmlRootElement annotation (otherwise, I'd need to define the parameter with the JAXBElement type).
#Path("/myService")
#POST
#Consumes(MediaType.APPLICATION_XML)
public void myService(MyDTO dto) throws Exception
{
// Shouldn't get this far if the XML content in the request was invalid
System.out.println(dto);
}
Requirement
By default, the Sun/Oracle JAXB implementation doesn't throw exceptions when the XML content has errors. For example providing a string value, say ABC, for an Integer attribute simply leaves the value as null instead of throwing an exception.
In JAXB 2 a ValidationEvenHandler can be defined. Using the following handler handler, makes the XML unmarshalling throw an exception the way I need it to.
public class UnmarshallerValidationEventHandler implements ValidationEventHandler {
#Override
public boolean handleEvent(ValidationEvent event) {
// This indicates JAXB that it should continue processing only if the
// severity level is less than error. NOTE: validation event constants
// go in ascending order in level of severity(i.e., 0 WARNING, 1: ERROR, 2 :FATAL_ERROR)
return event.getSeverity() < ValidationEvent.ERROR;
}
}
Question
How can I get Jersey to use a particular JAXBContext instance in order to use an unmarshaller with my custom validation event handler?
Alternatively, given that my application only uses JAXB in Jersey methods, defining a particular JAXBContext globally for the the JVM instance would be a good option. How could that be done?
Jersey Users Guide covers this in Using custom JAXBContext chapter. Basically you need to provide ContextResolver<T> like:
#Provider
public class PlanetJAXBContextProvider implements ContextResolver<JAXBContext> {
private JAXBContext context = null;
public JAXBContext getContext(Class<?> type) {
if(type != Planet.class)
return null; // we don't support nothing else than Planet
if(context == null) {
try {
context = JAXBContext.newInstance(Planet.class);
} catch (JAXBException e) {
// log warning/error; null will be returned which indicates that this
// provider won't/can't be used.
}
}
return context;
}
}
You can see a sample use in storage-service sample project (see JAXBContextResolver).
Note: Instead of ContextResolver<JAXBContext> you can also provide ContextResolver<Marshaller> or/and ContextResolver<Unmarshaller>.