|
#1
|
|||
|
|||
|
Hi, I'm back again to SI.
Since my last post here I had "stabilized" my work with SI M6, until I found what it seems a bug when calling methods thru the gateway that have no parameters. So I decided to upgrade to R2, to what my boss graciously give me one hour to do it. That's why I'm working on a Sunday on my own time... ![]() So apart from the classes that changed packages and schema configuration changes here are my *BIG* problems so far: 1) As I said in other posts, I'm using SI and Jersey (JAX-RS) at the same time. Now unfortunately it seems that SI forgot other annotations other than theirs, so it gets confused when it finds Jersey annotations. I solve that problem by changing MethodParameterMessageMapper (note the *******) Code:
boolean hasHeadersAnnotation() {
if (this._hasHeadersAnnotation) {
return true;
}
if (this.getParameterAnnotations() == null) {
return false;
}
for (Object o : this.getParameterAnnotations()) {
if (Headers.class.isInstance(o)) {
Assert.isAssignable(Map.class, this.getParameterType(),
"parameter with the @Headers annotation must be assignable to java.util.Map");
this._hasHeadersAnnotation = true; *******
return true; *******
}
}
return false;
}
2) Since I'm using the Gateway to map in-messages to receiver-side methods, I need to invoke methods with several params. But SI doesen't allow me to do it. I had to change again MethodParameterMessageMapper: Code:
private void initializeParameterMetadata() {
boolean foundMessageOrPayload = false;
Class<?>[] paramTypes = this.method.getParameterTypes();
this.parameterMetadata = new MethodParameterMetadata[paramTypes.length];
for (int i = 0; i < parameterMetadata.length; i++) {
MethodParameterMetadata metadata = new MethodParameterMetadata(this.method, i);
metadata.initParameterNameDiscovery(this.parameterNameDiscoverer);
GenericTypeResolver.resolveParameterType(metadata, this.method.getDeclaringClass());
if (metadata.getHeaderAnnotation() == null && !metadata.hasHeadersAnnotation()) {
// this is either a Message or the Object to be used as a Message payload
******* //Assert.isTrue(!foundMessageOrPayload, "only one Message or payload parameter is allowed");
foundMessageOrPayload = true;
}
parameterMetadata[i] = metadata;
}
}
Code:
public Message<?> toMessage(Object[] parameters) {
return new MyOldMessageCreator().createMessage(parameters);
}
Code:
if (paramCount == 0 ) {
if (shouldReply) {
if (isReturnTypeMessage) {
return gateway.receive();
}
response = gateway.receive();
}
}
else {
Code:
if (paramCount == 0 && false) {
Now this solve my problems and allows me to have the latest R2, but of course it's nothing but bad workarounds. However I hope you guys can pick on this and make decent changes. I still have to test another of my M6 problems, the propagation of a receiving-side exception to the requester-side, but I'll leave that for tomorrow. I hope this findings are useful. Cheers. Last edited by amsmota; Dec 5th, 2008 at 11:36 AM. |
|
#2
|
|||
|
|||
|
Thanks for posting all of these detailed explanations. I will go through each of these.
For starters, on #1, can you explain what you've changed? I'm not understanding the problem there (the code with ***** looks the same). Thanks, Mark
__________________
Mark Fisher Senior Software Engineer, SpringSource Spring Integration Lead http://www.springsource.com http://www.springsource.org/spring-integration http://blog.springsource.com/main/author/markf |
|
#3
|
|||
|
|||
|
Hi Mark.
It's the location of those two lines. In the original they are *outside* the if{} block, so they are executed every time even when the annotation is not a Header. Thanks for your quick review. Cheers. |
|
#4
|
|||
|
|||
|
Ah, I see now. That is definitely a bug. I just added the issue here: http://jira.springframework.org/browse/INT-492
...and I just committed the change as well: https://fisheye.springframework.org/...ration?cs=1760 Thank you for pointing this out. I will likely post a couple questions about your other two points later. Just one quick comment - the no-arg methods for a Gateway proxy are intended to be for receiving a Message. Can you describe what you are doing there? Thanks again, Mark
__________________
Mark Fisher Senior Software Engineer, SpringSource Spring Integration Lead http://www.springsource.com http://www.springsource.org/spring-integration http://blog.springsource.com/main/author/markf |
|
#5
|
|||
|
|||
|
Well, as I said elsewhere my use of SI is to provide a transparent service infrastructure that allow developers of services to write code "as usual" and them just extract and annotate a interface.
So they are free to write the code as they want, even code like Code:
class DataAcessor(){
public List getAllEmployees(){
getHibernateTemplate().getDao().readEmployees();
}
}
Code:
@Path("/dataservice")
interface DataAcessor(){
@Path("/dataservice/everybody")
@GET
List getAllEmployees();
}
Code:
http://somehost/services/dataservice/everybody That's why I can have calls (requests) without parameters, I can't put any constraint on the "server" side implementation. Feel free to ask what you want, I'm glad I can help, I wish I could contribute more .Cheers. Last edited by amsmota; Nov 23rd, 2008 at 05:58 PM. |
|
#6
|
|||
|
|||
|
I think I understand what you are building there, but it might be better to provide a gateway layer of your own rather than relying on the GatewayProxyFactoryBean.
The GatewayProxyFactoryBean is clearly focused on mapping a *single parameter* to a Message payload. The proxy methods may also accept additional parameters *if* annotated with @Header or @Headers. Those annotated parameters map to header values of the Message that is being created. And as you've discovered, any no-arg methods are intended for *receiving* Messages from a channel. It sounds like your use case might be better implemented with your own custom gateway layer that either builds upon the MessagingGateway hierarchy in Spring Integration or uses the MessageChannelTemplate directly. That said, I'm still interested in your thoughts - even if you completely disagree with my assessment here.
__________________
Mark Fisher Senior Software Engineer, SpringSource Spring Integration Lead http://www.springsource.com http://www.springsource.org/spring-integration http://blog.springsource.com/main/author/markf |
|
#7
|
|||
|
|||
|
Well, first let me stress this point here:
Quote:
Now for your considerations, I really don't think it's a question of me "agreeing" or not, those are architectural decisions that is up to the "owners" of the project to make. I have my requirements and use cases, that are most probably different from other people, that's the way its is and it's a normal thing... But from a "higher" point of view, I don't see no reason to impose architectural constraints like the ones you mention. Even more when is so simple to overcome those limitations. Here are some thoughts: - Just create a @Response annotation so you can identify methods that are intended for *receiving* Messages from a channel. That way you can have even methods *with* parameters (like a correlation Id, for instance) going directly to peek the response channel. - I don't see any problem on passing a Object or a Object[] as a payload. You have to write very few lines of code to allow that. You can even create a explicit annotation to make it more explicit, like @MultiParam or something. That way one can easily proxy methods with several parameters, that I think is a very usual thing to do. - Or actually allow the injection of a custom InboundMessageMapper (like the old MessageCreator) that simply leaves to the developer the decision of using or not multi-params. Just remove the current constraints that makes the single-param compulsory. Now I've done all this myself (and it took me half a hour to do it once I understood what was made) but just for my restricted use-case, so it's not a general solution. And again, the architectural decisions of your project are not mine to make ...
Last edited by amsmota; Nov 24th, 2008 at 08:30 AM. |
|
#8
|
|||
|
|||
|
Can you describe what you are doing with the no-param method that does create a Message? In general, I would expect the Message to contain something that maps from the inbound parameter. I guess in this case you have static Message content? Is there really a need to use Messaging for this use-case at all?
One thing that is interesting about this project (or any framework for that matter) is that what one user finds very intuitive for their use-case might be very confusing to someone else. Also, we are trying to stress that there is a difference between messaging-based architectures (with loose coupling and a single "document" object) as opposed to more tightly-coupled, difficult to maintain RPC solutions (e.g. an RMI method with multiple parameters). To keep things simple, the general rule we have followed with the gateway interface is that a single parameter can be a payload or Message - any other parameters must map to the MessageHeaders with annotations indicating how to map them. If any further Message-creation is needed for a certain use case, then extending SimpleMessagingGateway is still pretty simple, and it accepts custom mappers in its constructor: Code:
SimpleMessagingGateway(InboundMessageMapper<?> inboundMapper, OutboundMessageMapper<?> outboundMapper) { ... }
![]() Regards, Mark
__________________
Mark Fisher Senior Software Engineer, SpringSource Spring Integration Lead http://www.springsource.com http://www.springsource.org/spring-integration http://blog.springsource.com/main/author/markf |
|
#9
|
|||
|
|||
|
I think I already point my use. As a example I used a Class that works over Hibernate to access a DB.
Code:
public class EmployeesHibernateDAO{
Employee get EmployeeByName(String name){
...
{
Employee get EmployeeById(Long id){
...
{
List<Employee> getAllEmployees(){
...
}
}
Code:
public interface EmployeesHibernateDAO{
@GET
@Path("/data/employees/{name"})
Employee get EmployeeByName(String name);
@GET
@Path("/data/employees/{id"})
Employee get EmployeeById(Long id);
@GET
@Path("/data/employees)
List<Employee> getAllEmployees();
}
Now if you want to know what I'm doing internally on my SI based platform, is something like this: On the requester side: I have a Resource that has the Gateway (and thus the Interface) injected, parses the parameters and find which method of the interface should be called. Then it simply invoke Code:
method.invoke(gateway, parms.toArray()); On the receiving side: I have a MessageHandler accepting Messages from the InputChannel. This one has the Implementation of the Interface that was injected in the Gateway injected into it. It simply takes the method to be invoked on the injected implementation (that was put in the MessageHeader by my old MessageCreator), get's the payload and simply calls the method and wraps the response in a Message before sent it to the Response channel. Code:
Object response = method.invoke(serviceImpl, objs); return new GenericMessage<Object>(response); I have other questions that I'll write in another post, this one is already too long ...
|
|
#10
|
|||
|
|||
|
My question is really why you are using a Spring Integration Message to invoke the no-arg method. It seems like the messaging layer is being injected unnecessarily. Isn't this just a REST service that invokes a DAO?
__________________
Mark Fisher Senior Software Engineer, SpringSource Spring Integration Lead http://www.springsource.com http://www.springsource.org/spring-integration http://blog.springsource.com/main/author/markf |
![]() |
| Thread Tools | |
| Display Modes | |
|
|