PDA

View Full Version : Is there something missing in the SaoExporter?


steinard
09-04-2007, 09:09 AM
Hi!

We have used the SaoExporter to configure remoting services. In one special case the server will try to call back to the client (AsyncCallback) when the server get's updated through ActiveMq. This callabck failed when the service is configured through the exporter.

Exception:
This remoting proxy has no channel sink which means either the server has no registered server channels that are listening, or this application has no suitable client channel to talk to the server.


Stack:

at System.Runtime.Remoting.Proxies.RemotingProxy.Inte rnalInvoke(IMethodCallMessage reqMcmMsg, Boolean useDispatchMessage, Int32 callType)
at System.Runtime.Remoting.Proxies.RemotingProxy.Invo ke(IMessage reqMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateI nvoke(MessageData& msgData, Int32 type)
at Viz.Core.Integration.Priceboard.Server.DataSource. DataSourceMessageSubscriber.ReceiveMessageEventHan dler.BeginInvoke(String message, AsyncCallback callback, Object object)
at Viz.Core.Integration.Priceboard.Server.DataSource. DataSourceMessageSubscriber.ProcessMessage(IVizMes sage message) in c:\dev\iteration4.10\viz_enterprise_libs\Viz.Core. Integration.Priceboard\viz\core\integration\priceb oard\server\datasource\DataSourceMessageSubscriber .cs:line 81
at Spring.DynamicReflection.Method_ProcessMessage_34a 154c8894049869c7c2996492d4752.Invoke(Object target, Object[] args)
at Spring.Util.DynamicReflection.SafeMethod.Invoke(Ob ject target, Object[] arguments)
at Spring.Expressions.MethodNode.Get(Object context, EvaluationContext evalContext)
at Spring.Expressions.BaseNode.GetValueInternal(Objec t context, EvaluationContext evalContext)
at Spring.Expressions.Expression.Get(Object context, EvaluationContext evalContext)
at Spring.Expressions.BaseNode.GetValue(Object context, IDictionary variables)
at Spring.Expressions.BaseNode.Spring.Expressions.IEx pression.GetValue(Object context, IDictionary variables)
at Spring.Messaging.Nms.Listener.Adapter.MessageListe nerAdapter.OnMessage(IMessage message, ISession session)
at Spring.Messaging.Nms.Listener.Adapter.MessageListe nerAdapter.OnMessage(IMessage message)


But the AsyncCallback works fine when we configure it manually.


if (bool.Parse(ConfigurationManager.AppSettings["runAsServer"]))
{
RemotingServices.Marshal((MarshalByRefObject) GetDIObject(ConfigurationConstants.DEFAULT_SECURIT Y_SERVICE), "RemotedSaoSingletonSecurityService");
RemotingServices.Marshal((MarshalByRefObject) GetDIObject(ConfigurationConstants.DOMAIN_MODEL_SE RVICE_FACADE), "RemotedSaoSingletonDomainModelFacade");
}


Anyone have an idea what we are doing wrong here, or is there simply something missing in the exporter logic?

Cheers,
Steinar.

Mark Pollack
09-06-2007, 07:12 AM
Hi Steinar,

Is it possible that in the case of using the exporter you have not registered the channels before instantiating the container?

What type of object is the method GetDIObject returning? Is it a class that is inheriting from MarshalByRefObject?

Strange indeed. One way to solve it is not to use AsyncCallbacks. :) If you have NMS/ActiveMQ your architecture that would be a much more robust way to do async notification.

Cheers,
Mark

steinard
09-06-2007, 08:16 AM
Hi!

Is it possible that in the case of using the exporter you have not registered the channels before instantiating the container?
I thought about this yesterday, that maybe the NMS configuration starts before the remoting exporter has started in Spring's IoC container. Do you know if there is a way to ensure that something is always loaded in the end in the container (the opposite of IObjectFactoryPostProcessor)?

What type of object is the method GetDIObject returning? Is it a class that is inheriting from MarshalByRefObject?
The GetDIObject is just a wrapper method to get anything out of the container on request from some code. We have done this because we would like to wrap Spring.Net so if we decide to replace it with some other DI-framework, then it should be a matter of updating only one project.


/// <summary>
/// Retrieves a configurated instance from IoC container.
/// </summary>
public static object GetDIObject(string id)
{
if (null != appConfig) return appConfig.AppContext.GetObject(id);
else throw new ApplicationConfigurationNotInitializedException(
"The application context has not yet been initialized.");
}


Yes, all our services inherit from ContextBoundObject:

/// <summary>
/// A Serviceclass that can serve an entity(model) object.
/// Here we can put performance counters, transformation and other goodies
/// All calls to DAL should be routed through such objects
/// Objects that inherit from ContextBoundObject, have the ability to define rules
/// and properties that apply to calls that cross the context boundary created for that object.
/// The most obvious are providing services such as tracing, security and validation for
/// classes. Implementing these services in the context of the object means the services do not
/// impact the implementation of the object and the object does not impact the implementation of
/// the services
/// </summary>
public abstract class Service : ContextBoundObject, IService
{
private static readonly ILog log = LogManager.GetLogger(typeof(Service));

/// <summary>
/// Number of objects created
/// </summary>
protected static PerformanceCounter nrServiceObjectsCounter;

/// <summary>
/// Constructs a new service and increments number of service objects
/// </summary>
public Service()
{
if (nrServiceObjectsCounter != null)
{
nrServiceObjectsCounter.Increment();
}
}

/// <summary>
/// The main reason of implementing destructur is to decrement the object counters
/// </summary>
~Service()
{
// try to decrement the counter
try
{
if (nrServiceObjectsCounter != null)
nrServiceObjectsCounter.Decrement();
}
catch(Exception e)
{
if(log.IsDebugEnabled) log.Debug("Failed to destruct Service: "+e.GetBaseException());
}
}

/// <summary>
/// Retrieves tha security token that is associated to the running app by a given user
/// </summary>
/// <returns>Security token stirng</returns>
public string GetSecurityToken()
{
return EnterpriseApp.App.SecurityToken;
}


}// class Service


Do you think it is bad? I've read somewhere that you should avoid inheriting from ContextBoundObject...

Strange indeed. One way to solve it is not to use AsyncCallbacks. If you have NMS/ActiveMQ your architecture that would be a much more robust way to do async notification.
We do indeed use ActiveMQ and the NMS wrapper project. It was really simple to start using and I quite like it. I did not want the clients to be dependent on NMS as I wanted a uniform way for clients to get their data from the middleware. The middleware communicates though ActiveMQ with several adapters that in turn updates an identity map of products (e.g. stocks, orders, prices, deals and such). Now clients can access these data in the same way they would access data stored in a database by simply querying the identity map. Works quite well. In addition we would like to update clients based on updates in the identity map for data that the client(s) subscribes to. What do you make of such a design? Are there several drawbacks by using AsyncCallbacks?

Cheers,
Steinar.

steinard
09-07-2007, 04:23 PM
Hi!

Thanks for the input Mark. This was in fact an initialization problem, the component that were to execute the callback was initialized before the remoting service was initialized. Now I've changed the order in which the various configuration files are read, and now it works nicely even with the exporters.

I would really like to have better control over when objects are being created in the container. Is there a way to ensure that some object definition will always be loaded after the container has successfully created all the other objects?

Thanks,
Steinar.

Mark Pollack
09-07-2007, 04:29 PM
Hi Steinar,

Good to hear. I guess you meant to say "ensure that some object instance will always be created after the container has successfully created all the other objects?" (bold indicates my changes). I don't know if this helps the case you are talking about, but you can make some object lazy-init if you know you are going to refer to them at runtime only. Alternatively, you can use the depends-on attribute, to ensure ordering between when those objects are created by the container.

Cheers,
Mark

steinard
09-07-2007, 08:51 PM
Hi Mark!

Good to see that you understand what I mean even when I write something ambiguous!

I do not think I would like to chain or use depends-on on all my object definitions. I guess such chaining would solve my problem, but the simplest thing would be to implement a special interface, which the container would detect and then postpone the creation of that specific object definition until there are no other object definitions left to create (I guess lazy-init means creation on access, that would be too late in my case...).

I already do something similar for ensuring that a specific (and special) object definition is created first by implementing IObjectFactoryPostProcessor (I do not implement any of the methods there), and now I would like to do something similar at the end.

My concern is that someone might change the inclusion order of the configuration files and I would like to have some simple measures to avoid that:

<context>
<!-- Section for spring DI config. -->
<resource uri="file://~/Config/AssemblyConfig.xml"/>
<resource uri="file://~/Config/PersistenceConfig.xml"/>
<resource uri="file://~/Config/AopConfig.xml"/>
<resource uri="file://~/Config/ServicesConfig.xml"/>
<resource uri="file://~/Config/RemotingConfig.xml"/>
<resource uri="file://~/Config/EnterpriseAppConfig.xml"/>
<resource uri="file://~/Config/MessagingCfg.xml"/>
<resource uri="file://~/Config/PriceBoardServerConfig.xml"/>
</context>


Thanks,
Steinar.

Erich Eichinger
09-09-2007, 06:56 PM
Hi Steinar,

as usual you have the most interesting and challenging needs ;-)

But there's nothing Spring is not able to do - although there is nothing available out of the box, the application context instance raises an event to signal that it has finished processing its Objectfactory. Thus this event signals the very last stage of instantiating an application context.

Here's a possible solution:
Mark all objects that need to be instantiated at the very last state as "lazy-init='true'" and write a new class (e.g. "ForcedObjectInstantiator") that implements IApplicationEventListener and receives a list of objectnames it should force to be instantiated. As soon as the container raises the "Refresh" event, your instantiator simply calls GetObject() for each name on its list.

To maybe better illustrate the suggested strategy I attached some samplecode.

hope this helps,
Erich

steinard
09-09-2007, 10:29 PM
Hi Erich!

Yes, this helps, as your posts usually do! Thanks.

Cheers,
Steinar.

P.S
By the way, did you happen to glance at the code I sent you this summer?

Erich Eichinger
09-10-2007, 11:30 AM
Hi Steinar,

By the way, did you happen to glance at the code I sent you this summer?

I did - unfortunately I didn't have time to work it out. But it is a nice approach that I likely will follow in the Northwind example application.

Btw: Mark is working on a nice solution for exception handling via AOP. Thought I'd mention it...

cheers,
Erich

steinard
09-10-2007, 11:57 AM
Cool!

I can send you or Mark the three classes I made which make up the ExceptionManager... Nothing really fancy though...

Cheers,
Steinar.

steinard
09-13-2007, 01:16 PM
Hi!

I tried to make the simplest demo app for demonstrating a Spring.Net configured client - server with AsyncCallbacks for our development department. I've more or less copied the remoting configuration from the current working framework's startup projects, tweaked the configuration so that it fits for the little demo, but somehow it does not work and I get this exception:

Exception message: Cannot find requested service.


Server stack trace:
at System.Runtime.Remoting.Channels.BinaryServerForma tterSink.ProcessMessage(IServerChannelSinkStack sinkStack, IMessage requestMsg, ITransportHeaders requestHeaders, Stream requestStream, IMessage& responseMsg, ITransportHeaders& responseHeaders, Stream& responseStream)

Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleRe turnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateI nvoke(MessageData& msgData, Int32 type)
at Services.SimpleDummyService.ReceiveMessageEventHan dler.EndInvoke(IAsyncResult result)
at Services.SimpleDummyService.AsyncCallbackHandler(I AsyncResult ar) i C:\dev\AsyncCallbackTest\Services\SimpleDummyServi ce.cs:linje 69


The test app is extremely simple. First start the server, then start one client. The client will register it's callback handle to the server as a listener. Then start another client. When the second client enters the server, then all the clients (currently only one) should get a notification (callback) that another client has logged onto the server.

Obviously there is something in configuring AsyncCallbacks with Spring.Net that I do not understand (and why it suddenly works in the real app but not in this demo sample). Reading chapter 24 and 31 in the documentation did not enlighten me much for the callback case either. If you guys have time to take a look at my attached sample and maybe also expand the calculator example to demonstrate some sort of callback functionality, then I would be very grateful.

Thanks,
Steinar.

By the way, I had to strip away all the third party assemblies from the demo to upload the zipfile to this site.

steinard
09-19-2007, 08:44 AM
Hi!

I really need help with this issue. If I cannot get this configured via spring, then I have to hardcode the services to use remoting instead of pushing this into a config file and hiding it from the client and the server startup projects. I would really like to avoid that.

I also think that this could be useful for others writing thick winapps that are to communicate with a central middleware. Sometimes you have to introduce some kind of state that are to be held and the middleware must be able to communicate back to subscribing clients asynchronously when being updated from external systems. I guess this should be a quite common issue. There is a three class example in my previous post that demonstrates what goes wrong and also what information I lack to achieve this through configuration.

Thanks,
Steinar.

Erich Eichinger
09-19-2007, 12:03 PM
Hi Steinar,

this on top of my list (aside from my bred&butter job) ;-). I'll give it a shot on friday - unfort. I don't have time earlier and can't make any promises.

-Erich

steinard
09-19-2007, 12:26 PM
Thanks Erich!

I am truly greatful! I'll be working on researching this issue every evening and this weekend.

Regards,
Steinar.

Erich Eichinger
09-22-2007, 10:40 AM
Hi Steinar,

I've just given the code a quick look - unfort. I've not much time yet, so I can post only some hints :

1)
You are using a pre-RC1 version of Spring. I recommend upgrading.

2)
The line RemotingServices.Marshal( callbackClient ) is important! You need to register any object with remoting before you can hand out a remoting reference to it

3)
You should put a Console.ReadLine() in your ClientApp. Otherwise the process will most likely die before the server can call back. To make ReadLine() work, you must change your app to "ConsoleApplication" first of course

4)
Of course u can't start 2 clients on the same machine because they are using the same port.

I'll post a demo based on your code tonight. Hope this helps so far!

cheers,
Erich

steinard
09-22-2007, 12:51 PM
Hi!

Thanks for the clues! Can't believe I did not think about the port blocking problem when starting two clients on the same machine...ouch...

I do not know much about remoting (pretty obvious right!), it has (until now) kind of magically worked for me because Spring.Net config does everything...

I'll upgrade to RC1 soon, just have to get some Jira issues of my back. I saw there were some breaking changes and I guess I need some time to get things right if I change now (something I guess I won't have until the end of next week :( ).

I'll see what I can do,
thanks again Erich, I really do appreciate getting help here.

Cheers,
Steinar.

Erich Eichinger
09-22-2007, 12:56 PM
Hi Steinar,

I know about the Marshal() call, just because I had excactly the same issue a few months ago. Bruno helped me out then ;-)

btw: to just get your sample working it should not be required to upgrade.

-Erich

Erich Eichinger
09-22-2007, 02:28 PM
Hi all,

I attached a working and slightly improved version of your AsyncCallbackTest sample. Aside from the missing RemotingServices.Marshal() call, your solution was missing proper synchronization, since a remoting call may occur on any arbitrary thread.

This sample is built against the latest nightly of Spring.NET. See README.TXT in the .zip file for more info.

cheers,
Erich

steinard
09-22-2007, 08:35 PM
Hi Erich!

I'll download the improved and working version and take a look at it ASP. I'll just have to watch the mandatory Saturday night movie with my wife first (she'll get mad if she sees something that looks like code now!).

Thanks,
Steinar.

steinard
09-23-2007, 01:04 PM
Hi!

I've just had a look at the code. Thanks.

Now I'm wondering if it is possible to move the callback configuration into a config file instead of hard coding the callback in code?


...
// Set up the server binary formatter.
BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();
serverProvider.TypeFilterLevel = TypeFilterLevel.Full;

// Set up the server channel.
TcpChannel serverChannel =
new TcpChannel(argsChannel, new BinaryClientFormatterSinkProvider(), serverProvider);
...


And then let RemotingClientRegistry be injected into the SimpleDummyService so if my bosses wants me to change from remoting to something else (like dcom, not likely, but you never know...), then I should be able to reconfigure the app and swap implementation of RemotingClientRegistry?

What do you think,
thanks again,
Steinar.

Erich Eichinger
09-23-2007, 04:37 PM
Hi Steinar,

If you don't need tcp port probing, you can safely use an external config.
But port probing somehow needs to be hand-crafted - if you come up with a good idea, I'm always open ears.

Another issue with the code is the hard-coded call to "RemotingServices.Marshall()". I'm thinking of some interceptor that automatically checks, if a method argument needs to be marshalled. An attribute could be used to tag method arguments in remote interfaces:

public interface IService
{
void AccessService( [RemoteReference] ICallBack callBack);
}


just thinking loud...

-Erich

steinard
09-23-2007, 09:32 PM
Hi Erich,

I do not need port probing at all. The real usecase is described in this thread, in the third post I believe:

Self-quote...

We do indeed use ActiveMQ and the NMS wrapper project. It was really simple to start using and I quite like it. I did not want the clients to be dependent on NMS as I wanted a uniform way for clients to get their data from the middleware. The middleware communicates though ActiveMQ with several adapters that in turn updates an identity map of products (e.g. stocks, orders, prices, deals and such). Now clients can access these data in the same way they would access data stored in a database by simply querying the identity map. Works quite well. In addition we would like to update clients based on updates in the identity map for data that the client(s) subscribes to. What do you make of such a design? Are there several drawbacks by using AsyncCallbacks?


The example was more like a proof of concept that it is possible to use spring.net configuration for setting up the remote channels and initializing the remote async callback object. In the long run this is important as I would like to keep infrastructural concerns out of the code. By looking at the spring-docs, I could not find any information on how to configure async callbacks. Now I've started reading about .Net Remoting, because I see that I really need to understand more of what's going on here ;-)


void AccessService( [RemoteReference] ICallBack callBack);

Wow, so it is actually possible to tag method arguments? I'll have to check that out and find out how to use a tagged method. Never seen or done such a thing before. Is this possible in Java as well?

Thanks for giving me some new ideas here,
Cheers,
Steinar.

steinard
09-24-2007, 09:31 PM
Hi!

I got the same problem with commons logging and log4net in the sample you provided as described in this post: http://forum.springframework.net/showthread.php?t=3519

I have downloaded the latest source from CVS and compiled the assemblies and copied them to the 3rd_party_libs folder:
antlr.runtime.dll
Common.Logging.dll
Common.Logging.Log4Net129.dll
log4net.dll (version 1.2.10.0).
Spring.Core.dll
Spring.Services.dll

I tried to get the config to work, but ended up removing all logging cfg from app.config to get the sample to work. Are you sure nothing is broken?

Message: {"Could not configure Common.Logging from configuration section 'common/logging'."}

Here is the stack:

at Common.Logging.LogManager.BuildLoggerFactoryAdapte r()
at Common.Logging.LogManager.get_Adapter()
at Common.Logging.LogManager.GetLogger(Type type)
at Spring.Context.Support.ContextRegistry..cctor()


Any ideas?

Thanks,
Steinar.

Erich Eichinger
09-24-2007, 09:36 PM
Hi,

Common.Logging.Log4Net129 uses log4net 1.2.9
Common.Logging.Log4Net uses log4net 1.2.10

did I mix it up in the sample? Apologizes for that...

-Erich

steinard
09-24-2007, 09:38 PM
No problem, I did not spend much time on it :-)

I'll check it out right away.

-- Steinar.

steinard
09-24-2007, 09:48 PM
Hmmm, I got the same exception with Common.Logging.Log4Net as well...

-- Steinar.

Erich Eichinger
09-24-2007, 10:41 PM
strange - can u zip your solution and send me an email?

-Erich

steinard
09-24-2007, 10:53 PM
Okay... sending now...

-- Steinar.

Erich Eichinger
09-24-2007, 10:53 PM
Did u change the App.config from

<factoryAdapter type="Common.Logging.Log4Net.Log4NetLoggerFactoryAdapter , Common.Logging.Log4Net129">

to

<factoryAdapter type="Common.Logging.Log4Net.Log4NetLoggerFactoryAdapter , Common.Logging.Log4Net">


as well?

steinard
09-24-2007, 11:08 PM
No....

I just did it and it worked ;-)
So just relax,

Thanks,
Steinar.