Weblogic 12.2.1.2 - How to tune the object Serializer to use Jackson 2 and strange phenomena on jaxrs enablement for application/json endpoints - jackson

On Weblogic JEE 7 (version 12.2.1.2), I am finding it quite strange to understand the behavior of the application server in relation to the enablement and configuration of the JAX-RS web services.
The first point that I am trying to understand is a phenomena that I find quite inexplicable.
On a sample application where I was testing a trivial jax-rs web service where initially there were only to components:
Component (A)
#ApplicationPath("rest")
public class RestApplication extends Application {
private static final Logger LOGGER = LoggerFactory.getLogger(RestApplication.class);
// Class loading
{
LOGGER.info("Rest application static constructor invoked");
}
/**
* Default not arguments constructor
*/
public RestApplication() {
LOGGER.info("Rest application default constructore invoked.");
}
}
Which appropriately enables the base context for all rest endpoints you may want to use in your deployment.
Works exactly as in Wildfly - 100%.
So nothing to say about this component.
And then we enter the "swamp", component B, our rest endpoint.
There are several dummy apis on this dummy endpoint, we focus just on the most trivial of all.
#Path("/exampleRestEndpoint")
#Consumes({ "application/json", "text/html" })
#Produces({ "application/json" })
public class ExampleRestEndpoint {
private static final Logger LOGGER = LoggerFactory.getLogger(ExampleRestEndpoint.class);
#PostConstruct
public void postConstruct() {
LOGGER.info("Rest Endpoint constructed.");
}
#GET
#Path("/ping")
public BasicJsonResponseDto<String> ping() {
Instant date = Instant.now();
return new BasicJsonResponseDto<String>("Ping-service invoked successfully. " + date.toString());
}
}
I will go into details on the DTO class later at the bottom.
There are already points of discussion here.
The first point of discussion for me is the the metadata annotation at the top of the class that declares that for this endpoint we want to deliver back to the clients application/json.
But on my first writing of this annotation, I had in fact not added ANY such anotation.
So on my first implementation, the application server was working returning me a reply.
On a second step, when I added the annotation stating that the rest endpoint was to be returing applicaiton/json, weblogic stopped rendering any response complaining of the following:
SEVERE: MessageBodyWriter not found for media type=application/json,
type=class com.jersey.jaxb.Todo, genericType=class
com.jersey.jaxb.Todo.
Same experiment on wildfly, the endpoint worked immediately no major hassle.
The following stack overflow thread yielded me an answer that worked:
test serializations REST JAXRS
The fact that weblogic would complain that it knows of know body writers that are capable of redering this mime type is suprising to me, being that any JEE 7 container should be able of doing this out of the box... but ok!
On my pom for the sample application I pumped the following dependencies to be going into war, in the Web-inf/lib.
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.8.6</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
<version>2.8.6</version>
</dependency>
And problem solved.
The rest web service goes back to working.
On chrome I can control the http response metadata all perfect.
Funy thing now, when I deploy the same application and take away the jackson provider library, the rest endpoint continues to work without any problems.
Actually, I can no longer reproduce on my environment the weblogic complaint that I am not giving him any body writer that knows what to do with application/json replies.
So, this is my first question?
Does anybody know if for a basic rest service that deliver application/json where a sample application bundles no jackson library and jus uses jee7 apis, if weblogic is expected to be logging the error I quoted or not.
Does anybody have any sort of oncept/explanation for the fact that when I take away the dependencies things are just working as one would normally have expected from the start ... but not as one expect after having been forced to google a solution for the no body writer problem?
I really do not have an explanation for this...
Now the second point is to do with the class being used a return object on the /ping API i have quoted.
Originally, that endpoing API was returning a pure "String".
The ping message was not being wrapped in any sort of object.
And of course the result object was no real JSON object The return object was just a "some arbitrary string value".
So this of course is a problem for a rest client, that is expecting to de-serialize valid json and instead of getting an object or array, is getting pure raw data on the pipe.
For this reason, I then made the experiment of pumping the raw types like String and Integer under a support dto object.
Here is the implementation.
The implementation is important for the next question.
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = As.PROPERTY, property = "class")
public class BasicJsonResponseDto<T> implements Serializable {
T value;
public BasicJsonResponseDto() {
super();
}
public BasicJsonResponseDto(T value) {
super();
this.value = value;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
#Override
public String toString() {
return "BasicJsonResponseDto [value=" + value + "]";
}
}
For the sharper eyes, it will be immediately be obvious that when a response object to an endpoint is delivered via a class like this, the "de-serialization" logic is not a trivial task at all.
This wrapper object for the basic types Integer/String has a little be of poison in there.
Namely, the value T where we pump our ping string into gives no help for the de-serialization engine to figure out what object it is to expect to read out.
So the ping service, technically, is not trivial at all for the client-side code.
With that said, I now have two very different behaviors on Weblogic and Wildfly.
And the different must be directly related with the serialization technology that each is using.
When the ping service is executed on weblogic, right now, the reponse objects I get are of the form:
{"value":{"type":"string","value":"Ping-service invoked successfully.
2017-08-12T09:08:45.455Z"}}
So you see that whatever ObjectMapper weblogic is using and that I have definitely not configured, is by default pumping additional metadata aboute the T value, namely declaring it as a string.
(a) It is very cute that this done without my asking, but then on the client side I need to create an ObjectMapper to de-serialize that can interpret that same object medata.
Most likely there is an adequate set of annotation such as #JsonInfo with #JsonSubtype that I can add to the T value field to make the de-serialization work on the client side, but in terms of the DTO object that I have written I have not asked for any such metadata to go out.
(b) You will also notice, if you know jackson, that there is missing information on the object as well.
namely, I do not see the class="full.qualified.name" to my object.
But I have made sure I requested this metadata to go out on the response json by pumping this annotation into the class.
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = As.PROPERTY, property = "class")
So my question here is:
(1) What library exactly is weblogic using to serialize my json
(2) What default configurations do they have in there
(3) I have pumped into my pom.xml the "jackson-jaxrs-json-provider" dependency that originally make my endpoint work.
I have looked at the source code of this library, and it looks to me like this library tries to be in charge of the rendering of the json responses.
It does have a CDI #Provide #Consumes(/) Produces(/), and if I enable the debugger I can see during deployment that the jackson-jaxrs-json-provider gets invoked.
What I cannot see happening in weblogic is the ObjectMapper of this class ever getting called.
it is if I cannot override whatever default behavior is in charge right now.
Does anybody understand the behavior.
Are there ways to put the beast on a leash and take control of the situation?
Or do I have to trivialize the code to such a degree where my String wrapped object is named" StringWrappedDto and the value field is trviailized into String value.
I hope not, but right now I am struggling with this.
Now, we switch to Wildfly 10.1.0.
On Wildfly 10.1.0, my experience is things are going rather more according to expectation.
(a) If you deploy a WAR with just the RestApplication and the Endpoint I have provided.
Things work out of the box.
No hassle, no messages of incapacity to render application/json.
You are set to start working without taking control of any configuration.
(b) If you for example want to override the rendering behavior for Json, you can define your own ObjectMapper provider.
E.g.
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
private final ObjectMapper mapper;
public ObjectMapperContextResolver() {
mapper = new ObjectMapper();
mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
}
#Override
public ObjectMapper getContext(Class<?> type) {
return mapper;
}
}
And this guy here, if you debug it, you can see it get called on every call to the Ping service.
So you can take charge of how the json response will be serialized, if you need such level of control.
To conclude, on wildfly, the JSON response I get is what I expect as illustrated in the following quote:
{"class":"entrypoint.rest.BasicJsonResponse","value":"Ping-service
invoked successfully. 2017-08-12T16:42:13.788Z"}
I have tried setting breakpoints on almost every classon from jackson 2 that is involved in serialization of data, and literrally nothing gets hit on weblogic.
So I am pretty certain, something in the wlserver/modules is getting used.
Anyone has an idea of how to force jackson2 declared as dependency to the war to get used for serializaton and kill whatever default behavior is right now taking place?
--
It now starts being clear what is happening in the rendering.
I have written the following unit test as an experiment:
#Test
public void serializeJerseyTest() throws JsonProcessingException, JAXBException {
// (a) class to be marshalled into json
BasicJsonResponseDto<String> objectToSerialize = new BasicJsonResponseDto<String>(
"Ping-service invoked successfully. " + Instant.now().toString());
// (b) setup a jaxbcontext
Map<String, Object> properties = new HashMap<String, Object>(3);
JAXBContext jc = JAXBContext.newInstance(BasicStringJsonResponseDto.class);
// (c) Marshall to system out
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty("eclipselink.media-type", "application/json");
marshaller.setProperty("eclipselink.media-type", "application/json");
marshaller.marshal(objectToSerialize, System.out);
}
And the output of the unit test looks quite familiar...
{
"value" : {
"type" : "string",
"value" : "Ping-service invoked successfully. 2017-08-12T19:33:05.834Z"
}
Oracle is using Moxy to make the json...
So now the question is, how do I make it use jackson2 instead?
I am sure there is a way. Proably to be found on the jersey documentation, which I believe by default will want to use Moxy.
For future, reference, I will be now posting a debug stack trace that speaks volumes.
All the code I was trying to debug and could not find out how because I wasll the the time trying to set breakpoints on jackson when the implementation was jersey + moxy all along.
So here is the answer to that.
I came to this stack trace for the most rediculous reason in the world.
Moxy out of the box canont deserialize the JSON object it is producing for this web service.
Can anyone understand this?
That moxy is serializing me som json on the POJO but then the unmarshall cannot properly unsmarshall the value field?
So here is the stack trace:
Daemon Thread [[ACTIVE] ExecuteThread: '4' for queue: 'weblogic.kernel.Default (self-tuning)'] (Suspended (breakpoint at line 51 in entrypoint.rest.ObjectAdapter))
entrypoint.rest.ObjectAdapter.marshal(java.lang.Object) line: 51
entrypoint.rest.ObjectAdapter.marshal(java.lang.Object) line: 1
org.eclipse.persistence.internal.jaxb.XMLJavaTypeConverter.convertObjectValueToDataValue(java.lang.Object, org.eclipse.persistence.sessions.Session, org.eclipse.persistence.oxm.XMLMarshaller) line: 178
org.eclipse.persistence.oxm.mappings.XMLAnyObjectMapping.convertObjectValueToDataValue(java.lang.Object, org.eclipse.persistence.sessions.Session, org.eclipse.persistence.oxm.XMLMarshaller) line: 652
org.eclipse.persistence.oxm.mappings.XMLAnyObjectMapping.convertObjectValueToDataValue(java.lang.Object, org.eclipse.persistence.core.sessions.CoreSession, org.eclipse.persistence.internal.oxm.Marshaller) line: 1
org.eclipse.persistence.internal.oxm.XMLAnyObjectMappingNodeValue.marshalSingleValue(org.eclipse.persistence.internal.oxm.XPathFragment, org.eclipse.persistence.internal.oxm.record.MarshalRecord, java.lang.Object, java.lang.Object, org.eclipse.persistence.internal.core.sessions.CoreAbstractSession, org.eclipse.persistence.internal.oxm.NamespaceResolver, org.eclipse.persistence.internal.oxm.record.MarshalContext) line: 72
org.eclipse.persistence.internal.oxm.XMLAnyObjectMappingNodeValue.marshal(org.eclipse.persistence.internal.oxm.XPathFragment, org.eclipse.persistence.internal.oxm.record.MarshalRecord, java.lang.Object, org.eclipse.persistence.internal.core.sessions.CoreAbstractSession, org.eclipse.persistence.internal.oxm.NamespaceResolver, org.eclipse.persistence.internal.oxm.record.MarshalContext) line: 65
org.eclipse.persistence.internal.oxm.XMLAnyObjectMappingNodeValue(org.eclipse.persistence.internal.oxm.NodeValue).marshal(org.eclipse.persistence.internal.oxm.XPathFragment, org.eclipse.persistence.internal.oxm.record.MarshalRecord, java.lang.Object, org.eclipse.persistence.internal.core.sessions.CoreAbstractSession, org.eclipse.persistence.internal.oxm.NamespaceResolver, org.eclipse.persistence.internal.oxm.record.MarshalContext, org.eclipse.persistence.internal.oxm.XPathFragment) line: 102
org.eclipse.persistence.internal.oxm.record.ObjectMarshalContext.marshal(org.eclipse.persistence.internal.oxm.NodeValue, org.eclipse.persistence.internal.oxm.XPathFragment, org.eclipse.persistence.internal.oxm.record.MarshalRecord, java.lang.Object, org.eclipse.persistence.internal.core.sessions.CoreAbstractSession, org.eclipse.persistence.internal.oxm.NamespaceResolver, org.eclipse.persistence.internal.oxm.XPathFragment) line: 59
org.eclipse.persistence.internal.oxm.XPathNode.marshal(org.eclipse.persistence.internal.oxm.record.MarshalRecord, java.lang.Object, org.eclipse.persistence.internal.core.sessions.CoreAbstractSession, org.eclipse.persistence.internal.oxm.NamespaceResolver, org.eclipse.persistence.internal.oxm.Marshaller, org.eclipse.persistence.internal.oxm.record.MarshalContext, org.eclipse.persistence.internal.oxm.XPathFragment) line: 443
org.eclipse.persistence.internal.oxm.XPathObjectBuilder.buildRow(org.eclipse.persistence.internal.oxm.record.XMLRecord, java.lang.Object, org.eclipse.persistence.internal.core.sessions.CoreAbstractSession, org.eclipse.persistence.internal.oxm.Marshaller, org.eclipse.persistence.internal.oxm.XPathFragment) line: 243
org.eclipse.persistence.internal.oxm.TreeObjectBuilder.buildRow(org.eclipse.persistence.internal.oxm.record.XMLRecord, java.lang.Object, org.eclipse.persistence.internal.core.sessions.CoreAbstractSession, org.eclipse.persistence.internal.oxm.XMLMarshaller, org.eclipse.persistence.internal.oxm.XPathFragment) line: 118
org.eclipse.persistence.internal.oxm.TreeObjectBuilder.buildRow(org.eclipse.persistence.internal.oxm.record.XMLRecord, java.lang.Object, org.eclipse.persistence.internal.core.sessions.CoreAbstractSession, org.eclipse.persistence.internal.oxm.Marshaller, org.eclipse.persistence.internal.oxm.XPathFragment) line: 1
org.eclipse.persistence.oxm.XMLMarshaller(org.eclipse.persistence.internal.oxm.XMLMarshaller<ABSTRACT_SESSION,CHARACTER_ESCAPE_HANDLER,CONTEXT,DESCRIPTOR,MARSHALLER_LISTENER,MEDIA_TYPE,NAMESPACE_PREFIX_MAPPER,OBJECT_BUILDER,SESSION>).marshal(java.lang.Object, org.eclipse.persistence.oxm.record.MarshalRecord, ABSTRACT_SESSION, DESCRIPTOR, boolean) line: 766
org.eclipse.persistence.oxm.XMLMarshaller(org.eclipse.persistence.internal.oxm.XMLMarshaller<ABSTRACT_SESSION,CHARACTER_ESCAPE_HANDLER,CONTEXT,DESCRIPTOR,MARSHALLER_LISTENER,MEDIA_TYPE,NAMESPACE_PREFIX_MAPPER,OBJECT_BUILDER,SESSION>).marshalStreamOrWriter(java.lang.Object, org.eclipse.persistence.oxm.record.MarshalRecord, ABSTRACT_SESSION, DESCRIPTOR, boolean) line: 1147
org.eclipse.persistence.oxm.XMLMarshaller(org.eclipse.persistence.internal.oxm.XMLMarshaller<ABSTRACT_SESSION,CHARACTER_ESCAPE_HANDLER,CONTEXT,DESCRIPTOR,MARSHALLER_LISTENER,MEDIA_TYPE,NAMESPACE_PREFIX_MAPPER,OBJECT_BUILDER,SESSION>).marshal(java.lang.Object, java.io.OutputStream, ABSTRACT_SESSION, DESCRIPTOR) line: 934
org.eclipse.persistence.oxm.XMLMarshaller(org.eclipse.persistence.internal.oxm.XMLMarshaller<ABSTRACT_SESSION,CHARACTER_ESCAPE_HANDLER,CONTEXT,DESCRIPTOR,MARSHALLER_LISTENER,MEDIA_TYPE,NAMESPACE_PREFIX_MAPPER,OBJECT_BUILDER,SESSION>).marshal(java.lang.Object, java.io.OutputStream) line: 877
org.eclipse.persistence.jaxb.JAXBMarshaller.marshal(java.lang.Object, java.io.OutputStream) line: 496
org.glassfish.jersey.moxy.json.internal.ConfigurableMoxyJsonProvider(org.eclipse.persistence.jaxb.rs.MOXyJsonProvider).writeTo(java.lang.Object, java.lang.Class<?>, java.lang.reflect.Type, java.lang.annotation.Annotation[], javax.ws.rs.core.MediaType, javax.ws.rs.core.MultivaluedMap<java.lang.String,java.lang.Object>, java.io.OutputStream) line: 957
org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.invokeWriteTo(javax.ws.rs.ext.WriterInterceptorContext, javax.ws.rs.ext.MessageBodyWriter) line: 265
org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo(javax.ws.rs.ext.WriterInterceptorContext) line: 250
org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed() line: 162
org.glassfish.jersey.server.internal.JsonWithPaddingInterceptor.aroundWriteTo(javax.ws.rs.ext.WriterInterceptorContext) line: 106
org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed() line: 162
org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundWriteTo(javax.ws.rs.ext.WriterInterceptorContext) line: 86
org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed() line: 162
weblogic.jaxrs.server.internal.ChunkedOutputWriter.aroundWriteTo(javax.ws.rs.ext.WriterInterceptorContext) line: 65
org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed() line: 162
org.glassfish.jersey.message.internal.MessageBodyFactory.writeTo(java.lang.Object, java.lang.Class<?>, java.lang.reflect.Type, java.lang.annotation.Annotation[], javax.ws.rs.core.MediaType, javax.ws.rs.core.MultivaluedMap<java.lang.String,java.lang.Object>, org.glassfish.jersey.internal.PropertiesDelegate, java.io.OutputStream, java.lang.Iterable<javax.ws.rs.ext.WriterInterceptor>) line: 1130
org.glassfish.jersey.server.ServerRuntime$Responder.writeResponse(org.glassfish.jersey.server.ContainerResponse) line: 711
org.glassfish.jersey.server.ServerRuntime$Responder.processResponse(org.glassfish.jersey.server.ContainerResponse) line: 444
org.glassfish.jersey.server.ServerRuntime$Responder.process(org.glassfish.jersey.server.ContainerResponse) line: 434
org.glassfish.jersey.server.ServerRuntime$2.run() line: 329
org.glassfish.jersey.internal.Errors$1.call() line: 271
org.glassfish.jersey.internal.Errors$1.call() line: 267
org.glassfish.jersey.internal.Errors.process(java.util.concurrent.Callable<T>, boolean) line: 315
org.glassfish.jersey.internal.Errors.process(org.glassfish.jersey.internal.util.Producer<T>, boolean) line: 297
org.glassfish.jersey.internal.Errors.process(java.lang.Runnable) line: 267
org.glassfish.jersey.process.internal.RequestScope.runInScope(org.glassfish.jersey.process.internal.RequestScope$Instance, java.lang.Runnable) line: 317
org.glassfish.jersey.server.ServerRuntime.process(org.glassfish.jersey.server.ContainerRequest) line: 305
org.glassfish.jersey.server.ApplicationHandler.handle(org.glassfish.jersey.server.ContainerRequest) line: 1154
org.glassfish.jersey.servlet.WebComponent.serviceImpl(java.net.URI, java.net.URI, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 471
org.glassfish.jersey.servlet.WebComponent.service(java.net.URI, java.net.URI, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 425
org.glassfish.jersey.servlet.ServletContainer.service(java.net.URI, java.net.URI, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 383
org.glassfish.jersey.servlet.ServletContainer.service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 336
org.glassfish.jersey.servlet.ServletContainer.service(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 223
weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run() line: 286
weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run() line: 260
weblogic.servlet.internal.StubSecurityHelper.invokeServlet(javax.servlet.ServletRequest, javax.servlet.http.HttpServletRequest, weblogic.servlet.internal.ServletRequestImpl, javax.servlet.ServletResponse, javax.servlet.http.HttpServletResponse, javax.servlet.Servlet) line: 137
weblogic.servlet.internal.ServletStubImpl.execute(javax.servlet.ServletRequest, javax.servlet.ServletResponse, weblogic.servlet.internal.FilterChainImpl) line: 350
weblogic.servlet.internal.TailFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) line: 25
weblogic.servlet.internal.FilterChainImpl.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 78
weblogic.servlet.internal.RequestEventsFilter.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) line: 32
weblogic.servlet.internal.FilterChainImpl.doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) line: 78
weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.wrapRun(weblogic.servlet.internal.ServletStub, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) line: 3683
weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run() line: 3649
weblogic.security.acl.internal.AuthenticatedSubject.doAs(weblogic.security.subject.AbstractSubject, java.security.PrivilegedAction) line: 326
weblogic.security.service.SecurityManager.runAsForUserCode(weblogic.security.acl.internal.AuthenticatedSubject, weblogic.security.acl.internal.AuthenticatedSubject, java.security.PrivilegedAction<T>) line: 197
weblogic.servlet.provider.WlsSecurityProvider.runAsForUserCode(weblogic.security.acl.internal.AuthenticatedSubject, java.security.PrivilegedAction, weblogic.security.acl.internal.AuthenticatedSubject) line: 203
weblogic.servlet.provider.WlsSubjectHandle.run(java.security.PrivilegedAction) line: 71
weblogic.servlet.internal.WebAppServletContext.doSecuredExecute(weblogic.servlet.internal.ServletInvocationContext, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, boolean, boolean, boolean) line: 2433
weblogic.servlet.internal.WebAppServletContext.securedExecute(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, boolean) line: 2281
weblogic.servlet.internal.WebAppServletContext.execute(weblogic.servlet.internal.ServletRequestImpl, weblogic.servlet.internal.ServletResponseImpl) line: 2259
weblogic.servlet.internal.ServletRequestImpl.runInternal() line: 1691
weblogic.servlet.internal.ServletRequestImpl.run() line: 1651
weblogic.servlet.provider.ContainerSupportProviderImpl$WlsRequestExecutor.run() line: 270
weblogic.invocation.ComponentInvocationContextManager._runAs(weblogic.invocation.ComponentInvocationContext, java.lang.Runnable) line: 348
weblogic.invocation.ComponentInvocationContextManager.runAs(java.security.Principal, weblogic.invocation.ComponentInvocationContext, java.lang.Runnable) line: 333
weblogic.work.LivePartitionUtility.doRunWorkUnderContext(java.lang.Runnable, weblogic.invocation.ComponentInvocationContext) line: 54
weblogic.work.PartitionUtility.runWorkUnderContext(java.lang.Runnable, weblogic.invocation.ComponentInvocationContext) line: 41
weblogic.work.ServerWorkManagerImpl(weblogic.work.SelfTuningWorkManagerImpl).runWorkUnderContext(weblogic.work.ExecuteThread, weblogic.work.WorkAdapter) line: 640
weblogic.work.ExecuteThread.execute(weblogic.work.WorkAdapter) line: 406
weblogic.work.ExecuteThread.run() line: 346
I believe I will now search for the way to use jackson on weblogic, there is simply no way that I am going to be working around this issue by pumping the class full of jaxB annotation and on top of that be writing XmlAdapters for this.
Jackson can do all of this for free.
That is why when needed, Jackson will writen the #class attribute with all the neded metadata for the class to deserialize.
It simply cannot be this bad. Cannot be.

I got the same problem recently on weblogic 12.2.1.3 and it was MOXy all the way. Even if I specified in my ear to user JAX-RI through services in META-INF, weblogic was still loading MOXy.
I succeded to disable it setting the property:
import org.glassfish.jersey.CommonProperties;
import org.glassfish.jersey.server.ResourceConfig;
#ApplicationPath("api")
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
property(CommonProperties.MOXY_JSON_FEATURE_DISABLE, Boolean.TRUE);
///.. the rest of my setup
}
}

Well,
Weblogic is implementing JAX-RS via Jersey.
According to the jersey documentation, it should be a simple matter of pumping a dependency into your deployment to do the swtich from Moxy to Jackson.
E.g
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.26-b09</version>
<scope>runtime</scope>
</dependency>
See the following reference.
https://jersey.github.io/documentation/latest/media.html#json
Seems not to be so trivial, to do the switch, my weblogic behavior was still reflecting the usage of Moxy, even once I added the library on the jersey documentaiton. Perhaps I tried with the wrong version... I am not going to find this out today.
Since quite honestly, my patience has reached its limit for this petty problem.
I have decide to go with the flow. Leave Jersey to its default behavior let it Moxy. I will not get in the way.
I find Moxy rather more verbose than jackson, and I do not particularly fancy having to be spreading around jaxb.properties files like mushrooms where folders containing rest DTOS.
jaxb.properties example:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
In any case, right now I want consistency to exist between the outcome of a rest call on Weblogic and Wildfly. No way I am going to be writing if app server is Weblogic de-serialize it with A and if it is wildfly deserialize it with B. No way!
So what I did was quite simple.
Wildfly uses restEasy. And rest easy is easy! :)
So i simply went to my web.xml
And pumped the following information:
<context-param>
<param-name>resteasy.providers</param-name>
<param-value>org.eclipse.persistence.jaxb.rs.MOXyJsonProvider</param-value>
</context-param>
Thanks restEasy documentation, that was really helpful!
Finally, since I always use EclipseLink instead of Hibernate, the eclipselink module is already active whend df I startup wildfly. No need to activate it via jboss-deployment descriptor.xml.
However, the module was lacking sufficient metadata.
I had to pump into the module "org.eclipse.persistence" main.xml the following dependency:
<module name="javax.ws.rs.api" />
That means my module looks as follows:
<module xmlns="urn:jboss:module:1.3" name="org.eclipse.persistence">
<properties>
<property name="jboss.api" value="private"/>
</properties>
<resources>
<resource-root path="jipijapa-eclipselink-10.1.0.Final.jar"/>
<resource-root path="eclipselink-2.6.4.jar">
<filter>
<exclude path="javax/**" />
</filter>
</resource-root>
</resources>
<dependencies>
<module name="asm.asm"/>
<module name="javax.api"/>
<module name="javax.annotation.api"/>
<module name="javax.enterprise.api"/>
<module name="javax.persistence.api"/>
<module name="javax.transaction.api"/>
<module name="javax.validation.api"/>
<module name="javax.xml.bind.api"/>
<module name="org.antlr"/>
<module name="org.dom4j"/>
<module name="org.javassist"/>
<module name="org.jboss.as.jpa.spi"/>
<module name="org.jboss.logging"/>
<module name="org.jboss.vfs"/>
<!-- Add dependency on rest api -->
<module name="javax.ws.rs.api" />
</dependencies>
</module>
NOTE:
Just be careful maintaining such information, you want to automate hacking these files because you will lose track of this as time goes by.
Best is you create an installer for wildfly that given a vanilla zip does all your hacky tunings to the base metadata files, otherwise you are lost.
In any case, now Wildfly is rendering the output of rest calls based on Moxy and not based on Jackson.
This means I will have to refactor my rest client to be Moxy based, but quite honestly...
I am out of energy to struggle against weblogic.
I prefer Jackson, so much simpler and quicker to use, but hey... pick your battles right?
Editing informaiton:
some more information on weblogic:
On Weblogic, the following configuration is effective.
IF you write RestApplication class and override the public Map getProperties() you can add the following property:
proprties.put("jersey.config.server.disableMoxyJson", true);
And this will disable the Server behavior of using moxy and switch to jackson.
This has not effect on wildfly since only weblogic uses jersey and wildfly uses resteasy. Finally, to make a rest client you would do:
javax.ws.rs.client.ClientBuilder.newClient().register(Class.forName("org.glassfish.jersey.jackson.JacksonFeature"))

public class RestApplication extends Application {
#Override
public Map<String, Object> getProperties() {
Map<String, Object> map = new HashMap<>();
map.put("jersey.config.server.disableMoxyJson", true);
return map;
}
And also...
Remember to set proper headers in the HTTP-request.
I sent Http-Request without "Content-Type":"application/json" and the Weblogic webserver responded with 500 Internal Server Error.
Weblogic should have returned 400 Bad Request, but i dont think httpstatuscodes where so important for Oracle when they developed this webserver. Lol.

Related

How to log the MessageBodyReader used for a Web Service Call in Apache TomEE+

I own a few web services in a large code base. I see several custom MessageBodyReaders attached to my application.
I would like to know which one is used for a web service call.
I've looked around, but I can't seem to find a way to log that.
Your question is somewhat broad and therefore hard to answer. I'll try it anyways.
You have multiple services with multiple MessageBodyReader<>s, maybe distributed across several applications. While CXF itself supports tracing and message logging, there isn't a switch you can just turn on to see which MessageBodyReader implementations are readable and which not.
1. Approach - Adding logging to the implementing classes
One might think this is a good idea, and yes it is! On first sight.
Adding log outputs to every isReadable(..) method on every implementing class would solve your issue but is not practical, as it involved way too much manual logging and code changes.
2. Approach - AOP to the rescue!
Aspect Oriented Programming specifically addresses issue like that. You want to take actions (logging) over several classes and even log out, what happened in that classes. Those requirements are aspects as they don't add functionality to your classes, but to your program on a technical point of view.
It basically involves adding proxies around the isReadable(..) method on every MessageBodyReader implementation and log out whether it returned true or false.
Let's start
Assume a simple MessageBodyReader<InputStream> like the example I took from the CXF online documentation:
#Consumes("application/octet-stream")
#Provider
public class MyReader implements MessageBodyReader<InputStream> {
#Override
public boolean isReadable(Class<?> aClass, Type type, Annotation[] annotations, MediaType mediaType) {
return return (type == MyEntity.class);
}
public InputStream readFrom(Class<InputStream> clazz, Type t, Annotation[] a, MediaType mt,
MultivaluedMap<String, String> headers, InputStream is) throws IOException {
return new FilterInputStream(is) {
#Override
public int read(byte[] b) throws IOException {
return is.read();
}
};
}
}
To reach this class and every other implementation, we create an Advice with the following Pointcut:
execution(public boolean javax.ws.rs.ext.MessageBodyReader+.isReadable(..))
For the sake of simplicity we will just log out what was returned. Using Spring AOP this is ridiculously easy. We just use the #AfterReturning annotation:
#Slf4j
#Component
#Aspect
public class MyReaderLoggerAspect {
#AfterReturning(value = "execution(public boolean javax.ws.rs.ext.MessageBodyReader+.isReadable(..))", argNames = "joinPoint,called", returning = "called")
public void logReaderName(JoinPoint joinPoint, boolean called) {
log.info(String.format("MessageBodyReader %s executed: %s", joinPoint.getTarget().getClass().getName(), called));
}
}
And our logging output looks like:
2020-07-06 17:56:04.305 INFO 9204 --- [ restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-07-06 17:56:04.313 INFO 9204 --- [ restartedMain] com.example.demo.DemoApplication : Started DemoApplication in 2.424 seconds (JVM running for 3.676)
2020-07-06 18:02:03.480 INFO 9204 --- [ scheduling-1] c.e.demo.test.MyReaderLoggerAspect : MessageBodyReader com.example.demo.test.MyReader executed: false
2020-07-06 18:03:44.191 INFO 9204 --- [ scheduling-1] c.e.demo.test.MyReaderLoggerAspect : MessageBodyReader com.example.demo.test.MyReader executed: true
2020-07-06 18:03:44.192 INFO 9204 --- [ scheduling-1] c.e.demo.test.MyReaderLoggerAspect : MessageBodyReader com.example.demo.test.MyOtherReader executed: false
This is an easy approach to log out possible candidates for MessageBodyReaders and other automagically invoked implementation classes. You can even log out method parameters, change the invocation, and do much more.
What's next?
You may of course want to add a few more logging to your Advices, because the class name and a simple true or false won't fit real-world-needs, but this is a good starting point on putting some manual tracing to existent software architectures.
Mentioning Tracing - that's only a few steps from using common solutions like OpenTracing in your services. While it generally focuses on identifying bottle necks and issues in long chains of service calls, it would generally allow to answer questions like:
Why is this request taking so long?
What filter is involved in that call chain? <-- You are here
Where is this message processor involved?
Who would be affected by a major change in my service?
And so on

Sylius - How to implement a custom EntityRepository

I'm getting a bit frustrated trying to override the repository of my own Entity.
I need to create a custom repository method to get a list of my entities with special way. One queryBuilder with Having and OrderBy.
Te question is how can I setup my config to say Sylius, take my custom repositor, not the default.
I try this:
sylius_resource:
resources:
dinamic.category:
classes:
model: App\Bundle\SyliusBlogBundle\Entity\PostCategory
repository: App\Bundle\SyliusBlogBundle\Repository\PostCategoryRepository
This is my Repository:
<?php
namespace App\Bundle\SyliusBlogBundle\Repository;
use Doctrine\ORM\EntityRepository;
class PostCategoryRepository extends EntityRepository
{
public function findCategoriesMenu()
{
$queryBuilder = $this->createQueryBuilder('c');
return $queryBuilder
->addSelect('COUNT(p.id) as totalPosts')
->leftJoin('c.posts', 'p')
->andWhere('p.published = true')
->having('totalPosts > 0')
->addGroupBy('p.id')
;
}
}
When I try to use this method, Symfony throws me this error:
An exception has been thrown during the rendering of a template ("Undefined method 'findCategoriesMenu'. The method name must start with either findBy or findOneBy!")
Well you aren't subclassing the correct repository. The ResourceController expects a repository based on the Sylius\Component\Resource\Repository\RepositoryInterface. Since you are subclassing from Doctrine\ORM\EntityRepository that won't be the case.
Your repository should inherit from Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository (or implement the interface yourself).
I answer to the post to paste correctly the response of app/console debug:container dinamic.repository.category
Information for Service "dinamic.repository.category"
=====================================================
------------------ -------------------------------------------------------------------
Option Value
------------------ -------------------------------------------------------------------
Service ID dinamic.repository.category
Class Dinamic\Bundle\SyliusBlogBundle\Repository\PostCategoryRepository
Tags -
Scope container
Public yes
Synthetic no
Lazy no
Synchronized no
Abstract no
Autowired no
Autowiring Types -
------------------ -------------------------------------------------------------------
Since here all it's ok.
When i try to access to Posts list this error appears:
An exception has been thrown during the rendering of a template ("Catchable Fatal Error: Argument 4 passed to Sylius\Bundle\ResourceBundle\Controller\ResourceController::__construct() must implement interface Sylius\Component\Resource\Repository\RepositoryInterface, instance of Dinamic\Bundle\SyliusBlogBundle\Repository\PostCategoryRepository given, called in /Applications/XAMPP/xamppfiles/htdocs/rosasinbox-sylius/app/cache/dev/appDevDebugProjectContainer.php on line 2767 and defined")
The error of main post appears when the repository config wasn't set. Then my first post was wrong, on config.yml repository value wasn't set.
Now i set it another time and i got this error.
Sorry for the confusion.

WCF generated proxy throws InvalidOperationException due to multiple types with same name in WSDL

I'm using Visual Studio 2013 to generate a WCF service proxy from this WSDL file. However, as soon as I try to call the setSalesItemsV3 method, WCF throws an InvalidOperationException from deep in System.Xml.dll.
This sample project demonstrates the problem: https://github.com/jennings/WsdlDuplicateNameProblem
This is the inner exception:
Message: The top XML element 'start' from namespace '' references distinct types WsdlDuplicateName.SalesItemService.hsSimpleDate and System.DateTime. Use XML attributes to specify another XML name or namespace for the element or types.
I'm no expert at reading WSDL, but I've looked at it and the only sections that reference the name "start" are a few <wsdl:part> elements with name="start":
<wsdl:message name="setSalesItems">
<wsdl:part name="start" type="xsd:dateTime"></wsdl:part>
</wsdl:message>
<wsdl:message name="setSalesItemsV3">
<wsdl:part name="start" type="tns:hsSimpleDate"></wsdl:part>
</wsdl:message>
But, the parts are in completely different messages, so I don't see why there should be any confusion. I've run the WSDL file through several online WSDL validators and they seem to be okay with it.
Below is the only code in the project necessary to reproduce the problem (besides the generated proxy).
class Program
{
static void Main(string[] args)
{
SalesServiceClient client = new SalesServiceClient();
var date = ToSimpleDate(new DateTime());
// throws InvalidOperationException
// Message == "There was an error reflecting 'start'."
client.setSalesItemsV3(1, 1, null, date, date);
}
static hsSimpleDate ToSimpleDate(DateTime time)
{
return new hsSimpleDate
{
year = time.Year,
month = time.Month,
day = time.Day,
};
}
}
To demonstrate the problem let’s take a look into generated Reference.cs:
public partial class getSalesItemsV3 {
// skipped
[System.ServiceModel.MessageBodyMemberAttribute(Namespace="", Order=2)]
public WsdlDuplicateName.SalesItemService.hsSimpleDate start;
// skipped
}
public partial class setSalesItems {
// skipped
[System.ServiceModel.MessageBodyMemberAttribute(Namespace="", Order=3)]
public System.DateTime start;
// skipped
}
Please note that these elements have the same name (start) and the same namespace declared by the MessageBodyMember attribute ("", empty namespace). This cause "The top XML element 'start' from namespace '' references distinct types" serializer exception.
If we have this option:
(b) the changes I can make to the generated proxies to make the
serializer happy
... we can set namespaces for elements start, end and return (they all cause troubles) manually. I did it by myself and put the result here. You can paste it into your Reference.cs and serializer exception will gone.
But it seems that the root cause of your issue is that this service (http://services.hotschedules.com/api/services/SalesService?wsdl) is intended to be used through WebServices (and this problem is some kind of incompatibilities).
If you add reference to this server as a Web Reference (Add -> Service Reference... -> Advanced... -> Add Web Reference...) and write the same web method call, no problems with serialization will occur. Actually, in my case I received another kind of server exceptions in my test example, but it will solve your immediate serialization problem.
The mirror copy of your code, but using Web Service Reference (and not requires any changes in generated files) can be found here.
Hope this will help.
UPDATE: To found what is actually cause this problem we need to deep delve in XmlReflectionImporter source code. First, our WSDL using XSD schemas to define namespaces: http://www.w3.org/2001/XMLSchema for xsd and http://services.hotschedules.com/api/services/SalesService for tns. XmlReflectionImporter using NameTable (this is a wrapper for Hashtable) to store "accessors". Accessor is a pair of Namespace and Name.
Let's see source code that throws exception:
private Accessor ReconcileAccessor(Accessor accessor, NameTable accessors)
{
// initial check skipped
// look for accessor by name and namespace, add to accessors hash if not found and return
Accessor accessor1 = (Accessor) accessors[accessor.Name, accessor.Namespace];
if (accessor1 == null)
{
accessor.IsTopLevelInSchema = true;
accessors.Add(accessor.Name, accessor.Namespace, (object) accessor);
return accessor;
}
// accessor ("start" in our case) found!
// check if mappings is the same and return accessor. This is not our case, we have two accessors with the same name but different mappings (despite that this mappings is have the same type)!
if (accessor1.Mapping == accessor.Mapping)
return accessor1;
// next I skipped some reconciliations for MembersMapping and ArrayMapping. Please note that it performed by types, for example:
// if (accessor.Mapping is ArrayMapping) { /* some logic */}
// Our mapping is not MembersMapping or ArrayMapping and we finally got there:
throw new InvalidOperationException(Res.GetString("XmlCannotReconcileAccessor", (object) accessor.Name, (object) accessor.Namespace, (object) XmlReflectionImporter.GetMappingName((Mapping) accessor1.Mapping), (object) XmlReflectionImporter.GetMappingName((Mapping) accessor.Mapping)));
// Resource definition is: XmlCannotReconcileAccessor=The top XML element '{0}' from namespace '{1}' references distinct types {2} and {3}. Use XML attributes to specify another XML name or namespace for the element or types.
// using this resource template you can see that string representations of mappings are "WsdlDuplicateName.SalesItemService.hsSimpleDate" and "System.DateTime".
}
So, the main reconciliation logic is we can't have two accessors with the same name but different namespaces! There're may be some exceptions for MembersMapping and ArrayMapping types, but it is not our case.
I believe that this is some kind of a bug. The WSDL is correct and will pass validation, but due to this generic implementation of ReconcileAccessor from XmlReflectionImporter class we got an exception. Not sure if this is exact problem of XmlReflectionImporter, or may be there's another problem on a higher abstract layer. And, source generated by "Web Reference" is not using XmlReflectionImporter.
Another thing is worth to mention: generator puts a Namespace="" value for MessageBodyMemberAttribute, what is effectively break the reconciliation process. So, I believe there's some inconsistency or incompatibility.
Your problem might be how you are using the WSDL. Where I work we have older services that requires us to use wsdl.exe to generate the class files from the WSDL. We also use SoapUI to test our services. Without changing any of the WSDL or generated code I can make a request to that system.
Fiddler captures:
Outbound
POST http://services.hotschedules.com/api/services/SalesService HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 4.0.30319.18444)
VsDebuggerCausalityData: uIDPo7vfNRAHy8VFtfrdjickfDQAAAAAVvkpSjtKpEyy02P7sVr8C51Xoz163FNKvwhRT+6uA+wACQAA
Content-Type: text/xml; charset=utf-8
SOAPAction: ""
Host: services.hotschedules.com
Content-Length: 536
Expect: 100-continue
Proxy-Connection: Keep-Alive
<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><setSalesItemsV3 xmlns="http://services.hotschedules.com/api/services/SalesService"><concept xmlns="">1</concept><storeNum xmlns="">1</storeNum><start xmlns=""><day>1</day><month>1</month><year>1</year></start><end xmlns=""><day>1</day><month>1</month><year>1</year></end></setSalesItemsV3></soap:Body></soap:Envelope>
Inbound
HTTP/1.1 500 Internal Server Error
Server: Apache-Coyote/1.1
Content-Type: text/xml;charset=UTF-8
Content-Length: 366
Date: Thu, 26 Mar 2015 16:51:22 GMT
Proxy-Connection: Keep-Alive
Connection: Keep-Alive
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><soap:Fault><faultcode xmlns:ns1="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">ns1:InvalidSecurityToken</faultcode><faultstring>Error in SecurityHeader: An invalid security token was provided</faultstring></soap:Fault></soap:Body></soap:Envelope>
I received a 500 error from the system for not supplying security.
I see a few options:
use "add web reference" instead of "add service reference". I have verified it works. this will fallback into classic asp.net service proxy which is not as shiny as wcf but will do the work.
since there are only 6 methods (some seem dummy) you could import the wsdl 6 times into 6 different proxies (probably less). each time chnage the wsdl to contain only one operation (just delete the otehr operation tags, don't bother with messages/schema).
change the parameter names in the wsdl (start --> start1, start2...) and then in runtime build some message inspector that changes back (start1,start2-->start).
(not tested) I beleive you can refactor the WSDL such that instead of part elements each message will have one part called "parameter" which will direct to a wrapper xsd type with all of the original parts. you will build one wrapper per message. you can configure wcf to treat this as bare parameters and not emit the dummy wrapper element so on the wire it looks the same.
of course if you are in position to change the server that is best.
Every option has its pros and cons. Some will have a runtime overhead (#3) and some will complicate the design time. It also depends if you expect this WSDL to change and you will need to reimport it many times.

How can I POST (as XML) an object to my ApiController using RestSharp?

I have an ASP.NET MVC4 website implementing a REST API, which I'm consuming from a client application. My ApiController methods take and return complex objects, as XML.
I recently discovered RestSharp, and have begun moving my client project over to that. However, I'm having real problems with it. It seems to almost work - it's so close that I can almost taste success - but I just can't get it to work 100%.
The objects I'm passing across the wire look something like this:
// The object I'm passing across the wire
public class Example
{
bool IsActive { get; set; }
string Name { get; set; }
}
My ApiController method looks like this:
// My ApiController method
public HttpResponseMessage PostExample(Example example)
{
if (ModelState.IsValid)
{
db.Examples.Add(example);
db.SaveChanges();
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, example);
return response;
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
The problem occurs when I try to POST an object to my website, like this:
var example = new Example () { IsActive = true, Name = "foo" };
var request = new RestSharp.RestRequest("/api/example", RestSharp.Method.POST);
request.AddBody(example, XmlNamespace);
var client = new RestClient();
client.BaseUrl = "foo.com";
var response = client.Execute<Example>(request);
The code above does hit the PostExample method in my ApiController, and it has an Example object as the parameter. However the values of the properties of the Example object are not the same as I passed to the Execute method! In one case, the IsActive member was false instead of true, although I also saw a case where the Name member was null where it should have had a value.
I did some investigation using Fiddler, and it seems that the correct values are being created in the XML that RestSharp generates. However, the XML is not quite in the same format that the web server emits when doing a GET. The differences are subtle, but seem to make the difference between it working and not working. The framework at the web server end seems to be sensitive to these formatting differences, and is mis-interpreting the XML as a result.
Here's the XML I get from RestSharp:
<Example xmlns="http://schemas.datacontract.org/2004/07/ExampleNamespace">
<Name>foo</Name>
<IsActive>true</IsActive>
</Example>
This is what I get when doing a GET on the webserver (or when serializing using the DataContractSerializer, which is what I was previously doing):
<Example xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ExampleNamespace">
<IsActive>true</IsActive>
<Name>foo</Name>
</TagDto>
The RestSharp version has the following differences from the DataContractSerializer's version:
Fields are in a different order
RestSharp doesn't include the extra namespace XMLSchema-instance namespace
DataContractSerializer doesn't include any spaces or line-breaks (I added those above for readability)
I'm surprised that any of those make much of a difference, but clearly they do. Note also that until I added an explicit namespace in the AddBody call, this was missing in the generated XML (obviously), and the Example object passed into my ApiController was null.
Anyway, I noticed that RestSharp allows you to override the serializer, and provides a way to use the .NET XML serializer. I tried using that (to no avail).
This is what I added before the call to AddBody:
request.XmlSerializer = new RestSharp.Serializers.DotNetXmlSerializer(XmlNamespace);
..and this is what I get out:
<?xml version="1.0" encoding="utf-8"?>
<Example>
<Name>foo</Name>
<IsActive>true</IsActive>
</Example>
This is clearly no good, not least because it starts with an XML declaration, which I imagine would cause problems. There's no way to turn that off, because the RestSharp derived class provides no way to do so. Also, there's no namespace - and I can't get one to appear in the output no matter how I try to set the namespace in RestSharp (in the constructor for the DotNetXmlSerializer, by setting the Namespace member, or by passing in a namespace to AddBody). To my eyes, this class is nothing more than a trap.
It looks like my only option is to create my own serializer class and use the DataContractSerializer internally. Is that right, or am I missing something?
(BTW, I can set the RequestFormat of the request to JSON and it just works - but I'd still like to know how to get this working with XML).
I've had some issues with the AddBody calls not properly serializing JSON values, so there might be some similarity to your problem. Instead of AddBody, you could try:
request.AddParameter("text/xml", xmlAsString, ParameterType.RequestBody);
If that works, you could look to see about changing the second parameter to be the xml object and see if the serializer does what you want.
The other option could be the XmlMediaTypeFormatter.ReadFromStreamAsync isn't properly picking up a proper serializer; you could try overriding that function.
The issue above is because WebAPI is using the DataContractSerializer (as opposed to the XmlSerializer which is what you're after). To switch this around modify Global.asax as follows.
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
xml.UseXmlSerializer = true;
However, I suggest you use the RESTSharp formatters for WebAPI (instead of using the .Net formatters). This is particularly useful if you're DTO's have circular references (the .net fx serializers don't handle this too gracefully).
In Global.asax, modify the formatters by putting in
GlobalConfiguration.Configuration.Formatters.XmlFormatter = //RestSharp XML serializer here
A quick overview of serialization in WebAPI is here and worth a browse

JAX-RS return a Map<String,String>

I want to retrieve a Map from a using JAX-RS (text/xml)
#GET
public Map<String,String> getMap(){
}
but I am getting the error below:
0000001e FlushResultHa E org.apache.wink.server.internal.handlers.FlushResultHandler handleResponse The system could not find a javax.ws.rs.ext.MessageBodyWriter or a DataSourceProvider class for the java.util.HashMap type and application/x-ms-application mediaType. Ensure that a javax.ws.rs.ext.MessageBodyWriter exists in the JAX-RS application for the type and media type specified.
[10:43:52:885 IST 07/02/12] 0000001e RequestProces I org.apache.wink.server.internal.RequestProcessor logException The following error occurred during the invocation of the handlers chain: WebApplicationException (500 - Internal Server Error) with message 'null' while processing GET request sent to http://localhost:9080/jaxrs_module/echo/upload/getSiteNames
The solution I choose is to wrap a Map and use it for the return param.
#XmlRootElement
public class JaxrsMapWrapper {
private Map<String,String> map;
public JaxrsMapWrapper(){
}
public void setMap(Map<String,String> map) {
this.map = map;
}
public Map<String,String> getMap() {
return map;
}
}
and the method signature will go like this
#GET
public JaxrsMapWrapper getMap()
Your problem is that the default serialization strategy (use JAXB) means that you can't serialize that map directly. There are two main ways to deal with this.
Write an XmlAdaptor
There are a number of questions on this on SO but the nicest explanation I've seen so far is on the CXF users mailing list from a few years ago. The one tricky bit (since you don't want an extra wrapper element) is that once you've got yourself a type adaptor, you've got to install it using a package-level annotation (on the right package, which might take some effort to figure out). Those are relatively exotic.
Write a custom MessageBodyWriter
It might well be easier to write your own code to do the serialization. To do this, you implement javax.ws.rs.ext.MessageBodyWriter and tag it with #Provider (assuming that you are using an engine that uses that to manage registration; not all do for complex reasons that don't matter too much here). This will let you produce exactly the document you want from any arbitrary type at a cost of more complexity when writing (but at least you won't be having complex JAXB problems). There are many ways to actually generate XML, with which ones to choose between depending on the data to be serialized
Note that if you were streaming the data out rather than assembling everything in memory, you'd have to implement this interface.
Using CXF 2.4.2, it supports returning Map from the api. I use jackson-jaxrs 1.9.6 for serialization.
#Path("participation")
#Consumes({"application/json"})
#Produces({"application/json"})
public interface SurveyParticipationApi {
#GET
#Path("appParameters")
Map<String,String> getAppParameters();
....
}
With CXF 2.7.x use
WebClient.postCollection(Object collection, Class<T> memberClass, Class<T> responseClass)
,like this in your rest client code.
(Map<String, Region>) client.postCollection(regionCodes, String.class,Map.class);
for other collections use WebClient.postAndGetCollection().