Use JAXB to marshall bean in the service layer - jaxb

I use Jersey with JAXB to convert my response to XML. I have several methods that consume XML from the Request Body. I can specify the bean as a parameter to the method being called and JAXB will be used to automatically transform the request body data to that bean, or fail if it's not compatible.
This works great for the majority of all my cases, and I have not had the need to write a MessageBodyReader or MessageBodyWriter to handle the transformation to and from. I have this one instance where I need the request body to be mapped to the bean, but I also need the original XML that was in the body of the request. I need to store that in the DB.
I've tried getting to the request body through the HttpContext, and can't figure out how - I just see the header values and URI values. I tried through the HTTPServletContext as well ending in the same result.
Is there a way to capture the request body as it is before it's transformed or after? Will I need to create a MessageBodyReader to handle this case? It seemed like those were used to map the contents to a bean, which I already have functioning, so I didn't think that was the solution. But, perhaps in the MessageBodyReader I can put the request body into some variable and pass it along while still transforming the data to the beans. That still seems kind of more work than is needed. I would think I could get to the body through the request somehow.
Update:
I tried getting the body from ContainerRequest object using the getEntity method.
String xmlString = request.getEntity(String.class);
And, that worked great. The request body is captured in that property and I can add it to the request properties to access later in the resource, etc. The problem is, and I think it's because the entity is a stream, it clears out the body. So by the time the jaxb mapping to the beans happen, there is nothing to transform and I'm given a bad request response.
That didn't work but I wanted to throw it out there as a possibility if someone could get it working better.
Here is what I ended up doing, and it would be nice to not have to do it this way. I'm basically marshalling what was unmarshalled, in my service class.
final StringWriter st = new StringWriter();
try
{
final JAXBContext jaxbContext = JAXBContext.newInstance(full.classpath.and.classname.of.root.bean);
final Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(unmarshalled object, st);
} catch (final JAXBException je)
{
// TODO Auto-generated catch block
je.printStackTrace();
}
final String xml = st.toString();
So you can see - I basically take the object, the root bean that the body was unmarshalled to, and marshal it, giving the location of the annotated root bean to use in marhsalling.
It seems a bit redonkulous to have to marshal what was already unmarshalled, and if that is the response, maybe marshalled again on the way out. I would like a solution where I could grab a copy of the body before the unmarshalling happens. But in the meantime, this will have to work.

I ran into the same scenario and ended with similar conclusions, except I do it the other way around by getting it as a String and then marshal manually to avoid unnecessary JAXB work, e.g.:
String xmlString = request.getEntity(String.class);
Reader sReader = new StringReader(xmlString);
return (T)jaxbCtx.createUnmarshaller().unmarshal(sReader);

Related

No MessageBodyWriter for Single

I'm trying to use resteasy-rxjava2 to provide an XML document using jaxb, within a vertx application (using a non-vertx legacy library we have). But I get:
Could not find MessageBodyWriter for response object of type:
org.jboss.resteasy.rxjava2.propagation.ContextPropagatorOnSingleAssemblyAction$ContextPropagatorSingle of media type:
application/xml;charset=UTF-8
From what I can tell, this comes down to the difference between a MessageBodyWriter and the AsyncResponseProvider that is in the resteasy-rxjava2 dependency for a Single (SingleProvider).
I have the following resteasy service definition
#GET
#Path(FdsnwsPaths.QUERY)
#Produces(MediaType.APPLICATION_XML)
#Stream
// CHECKSTYLE:OFF too many parameters
public Response getQuery(...)
How do I get resteasy to properly serve the data asynchrously, using the SingleProvider or otherwise.
The #Get method must return the Single explicitly or it doesn't work. (Can't use Response or Object). In my case, the Single contains the jaxb xml root element.
#GET
#Path(FdsnwsPaths.QUERY)
#Produces(MediaType.APPLICATION_XML)
#Stream
public Single<FDSNStationXML> getQuery(...)
Then, to make things more complicated, in order to handle specific exception types and map them to specific response status codes, I have to create a custom ExceptionMapper which creates the Response object I used to be able to create directly in the method. (in resteasy-vertx, I found no documentation on how to do this, but in my case, I am creating my own VertxRestEasyDeployment object so I can register the class of the ExceptionMapper(s) like this:
VertxResteasyDeployment deployment = new VertxResteasyDeployment();
deployment.getActualProviderClasses().addAll(providerClasses);
For reference, this is all being done with:
RestEasy 5.0.3.Final (including resteasy-rxjava2)
RxJava 2.2.20
Vertx 3.9.5

Adding ConversionService to Spring Cloud Stream

I'm using Spring Integration and String Cloud Stream. I have a header that I want my HTTP gateway to use, which has a Long value, but it can't convert from Long to String by default and so displays the error Consider registering a Converter with ConversionService.
Therefore I tried adding my own LongToStringConverter class and the following Bean so that LongToStringConverter can be used:
#Bean
public ConversionService conversionService()
{
DefaultConversionService service = new DefaultConversionService();
service.addConverter( new LongToStringConverter() );
return service;
}
Then then received the following error: Dispatcher has no subscribers.
If I only return an instance of DefaultConversionService from the above bean I still receive the error.
When I remove the above bean and instead simply convert the Long value to String when setting the header value and that works without errors. Is it possible to use ConversionService instead? If so then how?
First of all there is already a ConversionService: https://docs.spring.io/spring-integration/docs/4.3.12.RELEASE/reference/html/messaging-endpoints-chapter.html#payload-type-conversion. And it has some set of predefined converters. So, you should consider to use #IntegrationConverter on the matter.
On the other hand it is unclear why do you need to do that at all. I wonder why Long.toString() isn't enough for you when you declare that header in first place.

XSD validation with XML reader, collecting validation errors. (C#)

I'm currently fighting with using an XMLSerializer to execute XSD validation and collect the validation errors in the files. The task is the validation of the file, based on custom XSD-s containing valueset information, presence information etc.
My problem is the following: when using the XMLReader it stops at the first error, if we attach a listener to the ValidationEvents of the reader (through XMLReaderSettings). So I simply catch the exception where I log the error. So far everything is fine, the problems start to appear after logging the exception. Right after that the XMLReader goes to the end tag of the failed field, but I cannot validate the next field due to an unexplained exception.
To put it in practice, here's my code where I catch the exception:
private bool TryDeserialize(XmlSerializer ser, XmlReader read,out object item)
{
string Itemname = read.Name;
XmlReader read2 = read.ReadSubtree();
try
{
item= ser.Deserialize(read2);
return true;
}
catch (Exception e)
{
_ErrorList.Add("XSD error at " + Itemname + ": " + e.InnerException.Message);
item = null;
return false;
}
}
This routine works well, but what follows is problematic. Assume I pass the following XML snippet to this code:
<a>2885</a>
<b>ABC</b>
<c>5</c>
Assume that 'b' may not have 'ABC' as a value, so I get an XSD error. At the end of this, the xmlreader will be at
'EndElement, Name=b'
from which I simply cannot move unless I get an exception. If I do xmlreader.read, then I get the following exception (cut the namespace here):
"e = {"The element 'urn:iso:.....b' cannot contain child element 'urn:iso:.....:c' because the parent element's content model is text only."}"
After this the xmlreader is at 'Element, Name=c', so it seems good, but when trying to deserialize it with the code above, I get the following exception:
'_message = "The transition from the 'ValidateElement' method to the 'ValidateText' method is not allowed."'
I don't really see how I may go over it. I tried without a second reader reading the subtree, but I have the same problem. Please suggest me something, I really am stuck. Thanks a lot in advance!
Greets
You may have to consider the following things:
In general, it is not always possible to "collect" all the errors, simply because validating parsers are free to abandon the validation process when certain types of errors occur, particularly those that put the validator in a state where it can't reliably recover. For e.g., a validator may still continue after running into a constraining facet violation for a simple type, but it'll skip a whole section if it runs in unexpected content.
Unlike parsing into a DOM, where the loading of a DOM is not affected by a validating reader failing validation, deserializing into an object is (or at least should be) totally different: DOM is about being well formed; deserializing, i.e. strong typing is about being valid.
Intuitively I would think that if you get a validation error, what is the point in continuing with the deserialization, and further validation?
Try validating your XML independent of deserialization. If indeed you get more errors flagged with this approach, then the above should explain why. If not, then you're chasing something else.

How can I tell Jersey to use my MessageBodyReader instead using JAXB?

Basically, I have some models which all use JAXB. However, I have some highly custom functionality to convert to JSON and back so I want to write my own MessageBodyReader/Writer to do the job for me.
Right now, the writing portion is done: if i return one of my models from a REST resource, it goes through my writer. But when I try and accept a model as a FormParam, it doesnt use my MessageBodyReader and instead attempts to unmarshal it using JAXB (which fails).
So how I can tell Jersey to use my Reader instead?
public TestModel testProvider(#FormParam("model") TestModel input){ //doesnt work
return new TestModel(); //this part works!
}
Mark as #Provider
Add the configuration to your web.xml EX:
<init-param><param-name>com.sun.jersey.config.property.packages</param-name> <param-value> your.package.that.contains.the.provider </param-value> </init-param>
Since your writer works, but your reader does not, I'm guessing you have just missed something in your configuration. Some things to check:
Do you have the #Provider annotation on your reader?
Did you correctly implement the isReadable method on your reader?
Do you have the appropriate #Consumes annotation on the reader, does its media type match the media type specified on your service method?

parameter in URL jsf2

I need to have this link:
http://myserver:/myproject/innerpage/clip.jsf&id=9099
to extract the id from a code like this:
HttpServletRequest request = (HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest();
String clipId = request.getParameter("id");
When I run it on tomcat I get:
message
/OnAir/innerpage/clip.jsf&id=9099
description The requested resource
(/OnAir/innerpage/clip.jsf&id=9099)
is not available.
When I run it without &id=9099 it runs all right.
How can I make it run?
The separator character between path and query string in URL is ?, not &. The & is separator character for multiple parameters in query string, e.g. name1=value1&name2=value2&name3=value3. If you omit the ?, then the query string will be seen as part of path in URL, which will lead to a HTTP 404 page/resource not found error as you encountered.
So, this link should work http://myserver:port/myproject/innerpage/clip.jsf?id=9099
That said, there's a much better way to access the request parameter. Set it as a managed property with a value of #{param.id}.
public class Bean {
#ManagedProperty(value="#{param.id}")
private Long id;
#PostConstruct
public void init() {
System.out.println(id); // 9099 as in your example.
}
// ...
}
The EL #{param.id} returns you the value of request.getParameter("id").
A tip: whenever you need to haul the "raw" Servlet API from under the JSF hoods inside a managed bean, always ask yourself (or here at SO): "Isn't there a JSF-ish way?". Big chance you're unnecessarily overcomplicating things ;)
You first have to show us how you are sending the parameter in your JSF, is it a commandButton/Link? An outputLink? A ? Also are you using redirect=true?
Chances are you are losing the id somewhere during the request.

Resources