`#RabbitListener` `#SendTo` Reply Not Working When Returning a `org.springframework.messaging.Message<?>` - spring-amqp

I've been successfully using a method that returns my business object with an #RabbitListener annotation.
The registered Jackson2JsonMessageConverter bean named messageConverter kicks in and perfectly converts both the incoming JSON message to my expected type and the returned business object to JSON.
// This works!!
#RabbitListener(...)
#SendTo("#{responseDestination})
MyBusinessResponseObject handle(MyBusinessRequestObject request) {
...
...
...
}
But now, when I start returning a org.springframework.messageing.Message<MyBusinessResponseObject>, then I get exceptions in the logs saying: SimpleMessageConverter only supports String, byte[] and Serializable payloads, received: com.xxx.xxx.xxx.domain.MyBusinessResponseObject
// This DOESN'T work
#RabbitListener(...)
#SendTo("#{responseDestination})
Message<MyBusinessResponseObject> handle(MyBusinessRequestObject request) {
...
...
...
}
I've tried explicitly referencing my SimpleRabbitListenerContainerFactory in the #RabbitLisener annotation's containerFactory property, but I still get the same error.
I'm not sure why a SimpleMessageConverter is kickking in in this case and where it is coming from.
How do I override that message converter that is being used to convert my new return type?
Thanks!

It's a bug; please open a JIRA Issue.

Related

Jenkins form validation with objects as parameters

I'm building a plugin for Jenkins and I'm trying to validate my form (connection test method). This worked fine when all #QueryParameter were Strings.
Now I'm trying to send my form validation method an Object like this:
public FormValidation doTestConnection(
#QueryParameter("url") final String url,
#QueryParameter("timeout") final String timeout,
#QueryParameter("bypassProxy") final boolean bypassProxy,
#QueryParameter("deployerCredentialsConfig") final CredentialsConfig deployerCredentialsConfig,
#QueryParameter("resolverCredentialsConfig") final CredentialsConfig resolverCredentialsConfig
) throws ServletException {
In my global.jelly file I have this:
<f:validateButton
title="${%Test Connection}" progress="${%Testing...}"
method="testConnection"
with="url,timeout,bypassProxy,deployerCredentialsConfig,resolverCredentialsConfig"/>
My CredentialConfig class implements Serializable but I guess that is not enough becuase I'm getting this when clicking the "Test Connection" button:
java.lang.IllegalArgumentException: Failed to invoke public hudson.util.FormValidation
org.jfrog.hudson.MyBuilder$DescriptorImpl.doTestConnection(java.lang.String,java.lang.String,boolean,org.jfrog.hudson.CredentialsConfig,org.jfrog.hudson.CredentialsConfig) throws javax.servlet.ServletException
Jenkins has no good documentation for using objects inside of FormValidation calls.
Looking at the Jenkins documentation and the code behind <f:validateButton/>, I believe it's impossible have objects bind in validation logic.
The docs say (https://wiki.jenkins-ci.org/display/JENKINS/Jelly+form+controls):
The 'with' attribute specifies the input fields sent to the server for
the validation. They are matched against the field attribute or the
name attribute of other input controls. The values of the nearest
input fields above the are sent to the server, so
this means the button has to come after the input fields. Multiple
fields can be specified by using ','.
The code simply gets fields by the names - there is no "object assembly" (I believe it's only done during actual config submission).
https://github.com/jenkinsci/jenkins/blob/96ec7a267e0efba2ec99590c871db0940e35920f/war/src/main/webapp/scripts/hudson-behavior.js#L2856
I bumped into a similar problem. Looking at the code, it seems stapler tries to convert your parameter to the type you provided in the doCheck function declaration.
class HandlerImpl extends AnnotationHandler<QueryParameter> {
public Object parse(StaplerRequest request, QueryParameter a, Class type, String parameterName) throws ServletException {
String name = a.value();
if(name.length()==0) name=parameterName;
if(name==null)
throw new IllegalArgumentException("Parameter name unavailable neither in the code nor in annotation");
String value = request.getParameter(name);
if(a.required() && value==null)
throw new ServletException("Required Query parameter "+name+" is missing");
if(a.fixEmpty() && value!=null && value.length()==0)
value = null;
return convert(type,value); // <--- HERE
}
}
As a workaround, I changed the type to boolean, like so:
public FormValidation doTestConnection(
#QueryParameter("url") final String url,
#QueryParameter("timeout") final String timeout,
#QueryParameter("bypassProxy") final boolean bypassProxy,
#QueryParameter("deployerCredentialsConfig") final boolean deployerCredentialsConfig,
#QueryParameter("resolverCredentialsConfig") final boolean resolverCredentialsConfig
) throws ServletException {
This allows me to at least check if the variable is set. It might not be enough for your use case, though.

Messages not reaching destination queue when using ServerInitializerFactory Netty 4

I am using apache camel netty4 in grails and I have declared mycustom ServerInitializerFactory as follows
public class MyServerInitializerFactory extends ServerInitializerFactory {
private int maxLineSize = 1048576;
NettyConsumer nettyConsumer
public MimacsServerInitializerFactory() {}
#Override
protected void initChannel(Channel channel) throws Exception {
ChannelPipeline pipeline = channel.pipeline()
pipeline.addLast("logger", new LoggingHandler(LogLevel.INFO))
pipeline.addLast("framer", new LengthFieldBasedFrameDecoder(ByteOrder.LITTLE_ENDIAN, maxLineSize, 2, 2, 6, 0, false))
pipeline.addLast("decoder", new MfuDecoder())
pipeline.addLast("encoder", new MfuEncoder())
pipeline.addLast("handler", new MyServerHandler())
}
}
I have a route which I setup as follows in my routebuilder.
from('netty4:tcp://192.168.254.3:553?serverInitializerFactory=#sif&keepAlive=true&sync=true&allowDefaultCodec=false').to('activemq:queue:Tracking.Queue')
My Camel Context is setup in the BootStrap.groovy as follows
def serverInitializerFactory = new MyServerInitializerFactory()
SimpleRegistry registry = new SimpleRegistry()
registry.put("sif", serverInitializerFactory)
CamelContext camelContext = new DefaultCamelContext(registry)
camelContext.addComponent("activemq", activeMQComponent.activeMQComponent("failover:tcp://localhost:61616"))
camelContext.addRoutes new TrackingMessageRoute()
camelContext.start()
When I run my app, my route is started and my framer, decoder, handler and encoders are all invoked but messages do not reach the Tracking. Queue and responses do not get back to the client.
If I do not use serverInitializerFactory in the netty url and user encoders and decoders instead, My messages are hitting the queue but I lose control of the acknowledgement that I want to sent for each type of message that I receive. It seems activemq tries to sent its own response which is rejected by my encoder.
Am I supposed to then write code to send again or is there something I am missing?
You need to add a handler with the consumer so it can be routed, see the unit test how its done:
https://github.com/apache/camel/blob/master/components/camel-netty4/src/test/java/org/apache/camel/component/netty4/NettyCustomPipelineFactoryAsynchTest.java#L112
I managed to get around that problem. In my channelRead0 method. I added the following lines
Exchange exchange = this.consumer.getEndpoint().createExchange(ctx, msg);
where ctx is the ChannelContextHandler and msg is the Message Object, the two are both parameters of the channelRead0 method.
I also added the following lines
this.consumer.createUoW(exchange);
and after my handling code I inserted the following line
this.consumer.doneUoW(exchange);
and everything works like a charm.

breeze entitiesWithErrors not found but instead found entityErrors

The error object that is returned from breeze manager saveChanges() don't have the array entitiesWithErrors, but instead has the entityErrors array (perhaps is as it is on breeze.js version: 1.4.12, metadataVersion: 1.0.5)
The returned error object looks like...
Error {stack: "Error: Client side validation errors encountered", entityErrors: Array[6], message: "Client side validation errors encountered see the Errors collection on this object for more detail" entityErrors: Array[6] bla. bla..
Thus the code bellow will fail and I will need to refactor it if I am not able to work with entitiesWithErrors
function getErrorMessages(error) {
function getValidationMessages(err) {
try {
return err.entitiesWithErrors.map(function (entity) {
return entity.entityAspect.getValidationErrors().map(function (valError) {
return valError.errorMessage;
}).join('; <br/>');
}).join('; <br/>');
} catch (e) {
}
return 'validation error';
}
var msg = error.message;
if (msg.match(/validation error/i)) {
return getValidationMessages(error);
}
return msg;
}
This breaking change was made in Breeze version 1.4.0. From the release notes,
The description of client side validation errors caught during a save
before posting to the server has changed.
Client side validation errors caught during a save, but before posting
to the server, cause the save to fail and be routed to the fail
promise. The fail promise returns an error object that contains a
description of the errors. This description has changed.
Previously this error object contained an entitiesWithErrors property
that contained a list of all of the entities that had failed
validation. This property has now been replaced with the entityErrors
property. The entityErrors property returns a collection of
entityError objects as described above.
This change was made in order to retain consistency between save
failures that occurred on the server and those that failed before
posting to the server on the client.
To refactor your code, you simply do,
return error.entityErrors.map(function (entityError) {
return entityError.errorMessage;
})

Breeze Todo Example - Why does the TodosController return a string on purge and reset?

I'm going over the TODOs example in Breeze for Angular and have a question which I am unable to find an answer for.
Why does the TodosController return a string on both purge and reset? It doesn't seem to matter if I return any string, null, or nothing at all...
Code snippet:
// ~/breeze/todos/purge
[HttpPost]
public string Purge()
{
TodoDatabaseInitializer.PurgeDatabase(_contextProvider.Context);
return "purged";
}
// ~/breeze/todos/reset
[HttpPost]
public string Reset()
{
Purge();
TodoDatabaseInitializer.SeedDatabase(_contextProvider.Context);
return "reset";
}
Thanks!
AJ
You are correct, it does not matter, in fact thi call has more to do with Entity Framework than breeze. The string is just a message for clarity and can be nice if you are using a tool like fiddler, which shows your http requests and the information about them. In this case, you would see purged in you http response body in fiddler, so you know the call succeeded. Doesn't look like it's used on the client though.

SoapExtensions are used only for ASP.NET

I'm trying to implement a SoapExtension for log purposes (print the xml soap request) on an .NET 2.0 client application (not ASP.NET).
I first tried a simple console application just to check and I'm not able to see that the extension is called.
The simple code is just two lines:
System.Net.WebRequest request = System.Net.WebRequest.Create("http://www.ynet.com");
WebResponse response = request.GetResponse();
and my config file is the following:
What I'm doing wrong? are the extension usable only in ASP.NET?
Thanks
You must have class e.g.:"YourExtension" derived from "SoapExtension" class and attribute class e.g. "YourExtensionAttribute" derived from "SoapExtensionAttribute" there you overide "Type" property:
public override Type ExtensionType
{
get { return typeof(YourExtension); }
}
and put SoapExtensionAttribute to your webservice method

Resources