Community   SpringSource   Projects    Downloads    Documentation    Forums    Training   Exchange   Blogs

Go Back   Spring Community Forums > Core Spring Projects > Spring Integration

Reply
 
Thread Tools Display Modes
  #1  
Old Nov 23rd, 2008, 02:25 PM
amsmota amsmota is offline
Senior Member
 
Join Date: Feb 2008
Location: Dublin - Ireland
Posts: 101
Default Upgrading M6 to RC2 / RELEASE

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;
		}
Now this seems to me like a bug that can cause problems in other situations besides mine.

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;
		}
	}
3) Now in my M6 Gateway I was relying on my own MessageCreator that was being injected in it. But now it seems that the MessageCreator is gone, and I didn't find a way to change the default InboundMessageMapper on, again, MethodParameterMessageMapper. So I've made a very ugly hack:

Code:
	public Message<?> toMessage(Object[] parameters) {
		return new MyOldMessageCreator().createMessage(parameters);
       }
Finally, after all this I found that my original problem still happens, and again it looks to me like a bug. Looking at this code from GatewayProxyFactoryBean.invokeGatewayMethod
Code:
		if (paramCount == 0 ) {
			if (shouldReply) {
				if (isReturnTypeMessage) {
					return gateway.receive();
				}
				response = gateway.receive();
			}
		}
		else {
it seems that when a method has no params SI tries to *receive* the response method withou never *send* the request message, and thus it keeps waiting forever for the response. Since I have no time for more, I just made another very ugly hack
Code:
if (paramCount == 0 && false) {
and like that it works.

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.
Reply With Quote
  #2  
Old Nov 23rd, 2008, 02:50 PM
Mark Fisher Mark Fisher is offline
Senior Member
Spring Team
 
Join Date: Oct 2005
Location: Boston, MA
Posts: 1,355
Default

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
Reply With Quote
  #3  
Old Nov 23rd, 2008, 03:36 PM
amsmota amsmota is offline
Senior Member
 
Join Date: Feb 2008
Location: Dublin - Ireland
Posts: 101
Default

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.
Reply With Quote
  #4  
Old Nov 23rd, 2008, 04:31 PM
Mark Fisher Mark Fisher is offline
Senior Member
Spring Team
 
Join Date: Oct 2005
Location: Boston, MA
Posts: 1,355
Default

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
Reply With Quote
  #5  
Old Nov 23rd, 2008, 05:50 PM
amsmota amsmota is offline
Senior Member
 
Join Date: Feb 2008
Location: Dublin - Ireland
Posts: 101
Default

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();
   }
}
and (using Jersey annotations)
Code:
@Path("/dataservice")
interface DataAcessor(){

   @Path("/dataservice/everybody")
   @GET
   List getAllEmployees();
}
so that the code can be accessed remotely, using a REST-like service, by send a GET request to a resource identified by

Code:
http://somehost/services/dataservice/everybody
thru a connector that in this situation will be a HttpConnector (but can be other protocols as well).


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.
Reply With Quote
  #6  
Old Nov 23rd, 2008, 06:38 PM
Mark Fisher Mark Fisher is offline
Senior Member
Spring Team
 
Join Date: Oct 2005
Location: Boston, MA
Posts: 1,355
Default

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
Reply With Quote
  #7  
Old Nov 24th, 2008, 05:26 AM
amsmota amsmota is offline
Senior Member
 
Join Date: Feb 2008
Location: Dublin - Ireland
Posts: 101
Default

Well, first let me stress this point here:

Quote:
3) Now in my M6 Gateway I was relying on my own MessageCreator that was being injected in it. But now it seems that the MessageCreator is gone, and I didn't find a way to change the default InboundMessageMapper on, again, MethodParameterMessageMapper. So I've made a very ugly hack:
because this is a thing I really need and my hack is indeed a very ugly one.

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.
Reply With Quote
  #8  
Old Nov 24th, 2008, 08:40 AM
Mark Fisher Mark Fisher is offline
Senior Member
Spring Team
 
Join Date: Oct 2005
Location: Boston, MA
Posts: 1,355
Default

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) { ... }
However, I'm interested in hearing your answer to my first question above (no-arg method). I'm concerned to some degree that it may be a solution looking for a problem. Of course, I'm often proven wrong

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
Reply With Quote
  #9  
Old Nov 24th, 2008, 09:56 AM
amsmota amsmota is offline
Senior Member
 
Join Date: Feb 2008
Location: Dublin - Ireland
Posts: 101
Default

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(){
     ...
   }
}
From here I extract a Interface that I annotate with Jersey (but could be any other kind of annotations, including my own)


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 I just feed the Gateway with this interface and that is all I have to do to have a remote service requesting those paths to invoke the service. Now this has nothing to do with RMI, it's pure REST style, being the advantage the services being written as if it were to be invoked locally. No need to be concerned about REST or Messaging at all.

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());
Since this is a method of the Interface that is wrapped within the Gateway, all the messaging stuff is done behind the scene and I finally have a Message with a array of parms (so, any number of parms, even if it's a empty array) sent thru SI to the InputChannel defined in the Gateway. With 0, 1 or more Objects as a payload.

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);
So this a solution that already found it's problem, my problem, and has I said it' already done with the changes I've made, so if you think these ideas don't apply to the overall design of SI, that's fine by me, I just use my implementation over the existing SI.

I have other questions that I'll write in another post, this one is already too long ...
Reply With Quote
  #10  
Old Nov 24th, 2008, 10:01 AM
Mark Fisher Mark Fisher is offline
Senior Member
Spring Team
 
Join Date: Oct 2005
Location: Boston, MA
Posts: 1,355
Default

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
Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -5. The time now is 10:11 AM.


Contegix provides first-class managed hosting and partial sponsorship of these forums.

Powered by vBulletin® Version 3.8.4
Copyright ©2000 - 2010, Jelsoft Enterprises Ltd.