[en] Nice work!

Topics: Technical Support
Coordinator
Feb 20, 2011 at 9:10 PM

Hi!

Zyan framework looks very promising! I'm impressed with its events support (event handling has always been a bit tricky in .NET Remoting) and the ability to publish components not derived from MarshalByRefObject. Pluggable session management and custom authentication are also quite handy features.

Sadly, most of documentation, discussions and all code comments are in German. I had to use google translation service, so I'm not sure I've got everything right. Here are a few questions:

— Is it possible to host generic components or components with generic methods?

— One common misbehaviour of standard TcpChannel is that it opens new connections for server-to-client callbacks,so clients behind NAT or Firewall don't receive anything (this issue is addressed by a few third-party frameworks like Genuine Channels). Is is possible for ZyanConnection class to reuse tcp connection initiated by client to handle callbacks (for example, to deliver events raised by hosted component)?

— Am I able to write custom message sinks for hosted components?

— It is possible to intercept certain remote calls to handle them locally (for example, to cache results)?

— What about load balancing scenarios (dispatching calls to numerous hosts using single client proxy)?

— As far as I can understand, subscription is canceled once event dispatching is failed. Is it possible to plug in custom subscription management strategy (for example, to retry delivery a few times before canceling subscription)?

— What about ambient transaction support? For example:

using (var scope = new TransactionScope())
{
	// proxy1 and proxy2 share the same ZyanConnection
	proxy1.SaveSomething();
	
	if (proxy2.CheckStuff())
		scope.Complete(); // commit transaction
}

Regards, yallie

Coordinator
Feb 21, 2011 at 8:10 AM
Edited Feb 21, 2011 at 8:36 AM

Hello yallie,

thanks for your interest in my project.

> Is it possible to host generic components or components with generic methods?

Generic components: Yes
Components with generic methods: No.

> One common misbehaviour of standard TcpChannel is that it opens new connections for server-to-client callbacks,so clients behind NAT or Firewall don't receive anything (this issue is addressed by a few third-party frameworks like Genuine Channels). Is is possible for ZyanConnection class to reuse tcp connection initiated by client to handle callbacks (for example, to deliver events raised by hosted component)?

At the current Version only the default TcpChannel, HttpChannel and IpcChannel supported. But I plan to include the bi-directional channel from Ingo Rammer in version 2.0.  If you have already a commerical channel from Genuine Channels e.G., you can plug it easily into Zyan. Zyan is capsulating the channel creation and configuration in ProtocolSetup classes. All you have to do for plugging a new channel is to implement IClientProtocolSetup and IServerProtocolSetup.
I try to roll out the next major version as soon as possible.

> Am I able to write custom message sinks for hosted components?

You can write .NET Remoting message sinks and plug them in. Plugging message sinks is also done inside your custom ProtocolSetup classes (CreateChannel method). Zyan uses the existing .NET Remoting infrastructure. But keep in mind, that Zyan does not invoke the remote components directly as Remoting does. Zyan forward all calls to the serverside ZyanDispatcher. This must be addressed, when writing custom message sinks.

> It is possible to intercept certain remote calls to handle them locally (for example, to cache results)?

No, but I can implement that in Zyan. Currently you are able to handle BeforeInvoke and AfterInvoke events from the ZyanConnection instance. You can do logging stuff e.G. there and cancel remote invocation, if you want. But replacing with a local call is not possible today. But this seems to be a handy feature and I will implement it in a future release.

> What about load balancing scenarios (dispatching calls to numerous hosts using single client proxy)?

You can use the Network Load Balancing Service from Windows Server to accomplish that. But you should use SqlSessionManager instead ob InProcSessionManager and only use SingleCall activation.

> As far as I can understand, subscription is canceled once event dispatching is failed. Is it possible to plug in custom subscription management strategy (for example, to retry delivery a few times before canceling subscription)?

No.

> What about ambient transaction support? For example:

Support for ambient transactions is included. You can turn it on in per proxy: connection.CreateProxy<IServerComponent>(true);

Best Regards,

Hagen

 

Coordinator
Feb 21, 2011 at 7:31 PM

Hello yallie,

I´ve created a feature request for the call interception story inside the Zyan issue tracker.
You can find it here: http://zyan.codeplex.com/workitem/663

Please use the notification option of the issue tracker to monitor changes of your requested feature.

Regards, Hagen

Coordinator
Feb 21, 2011 at 9:35 PM

Hi, Rainbird,

Thanks for reply!

>I plan to include the bi-directional channel from Ingo Rammer in version 2.0. 

AFAIK, Ingo Rammer's channel has never been updated since FX 1.0. Some users reported that it doesn't work well under FX 1.1 or later. It may need major redesign, I suppose.

>No, but I can implement that in Zyan. 

That would be nice!

>You can use the Network Load Balancing Service from Windows Server

Thanks for the suggestion, I'll check it out... For the first glance it doesn't seem like a good solution for enterprise-level application server, though. I was thinking about some customizable load-balancing strategy (like skipping offline servers, routing messages to least-loaded server where 'load' is some heuristic quotient calculated on-the-fly, etc). Network Load Balancing Service doesn't seem to support anything like that.

>Support for ambient transactions is included. 

That's excellent!

>I've created a feature request for the call interception story inside the Zyan issue tracker.

Thanks a lot!

Coordinator
Feb 22, 2011 at 12:32 AM

Hi, yallie,

> AFAIK, Ingo Rammer's channel has never been updated since FX 1.0. Some users reported that it doesn't work well under FX 1.1 or later. It may need major redesign, I suppose.

Yep, plugging in this old channel may cause a lot of work for me to do. But Ingo Rammer´s channel is the best free availabe template for a bi-directional channel. .NET Remoting hasn´t really changed since .NET Framework 1.0. The most trouble in FX 1.1 was caused by the restrictive new type filter, which was switched on by default. Generics may be another pitfall for this old channel. I´ll see. But when i finally get it done, Zyan can fire events easily back through client firewalls.

> For the first glance it doesn't seem like a good solution for enterprise-level application server, though. I was thinking about some customizable load-balancing strategy (like skipping offline servers, routing messages to least-loaded server where 'load' is some heuristic > quotient calculated on-the-fly, etc). Network Load Balancing Service doesn't seem to support anything like that.

Sounds interesting. But the Zyan.Communication.dll is not a place for such load-balancing logic. I think a seperate Windows Service Project is needed to implement that. When the communication framework Zyan is feature complete some day, I will think about starting a Zyan Server Project.

Coordinator
Feb 22, 2011 at 2:52 PM

>Generics may be another pitfall for this old channel.

I suspect, channel itself has nothing to do with generics, object serialization, etc. It just manages connections and transfers data packets across network.

>Zyan.Communication.dll is not a place for such load-balancing logic.

Of course, I didn't mean including load-balancing logic into Zyan framework itself! Such logic is always application-specific.

Coordinator
Feb 22, 2011 at 11:58 PM

> I suspect, channel itself has nothing to do with generics, object serialization, etc. It just manages connections and transfers data packets across network.

Of course, you´re right. That was an error in reasoning of mine.

Coordinator
Feb 23, 2011 at 11:41 PM
Edited Feb 23, 2011 at 11:43 PM

Good news :)

I finally found TcpEx remoting channel which was lost since GotDotNet site ceased to exist back in 2007.
TcpEx is another open-source bidirectional tcp channel and it's supposedly much more stable than Ingo Rammer's channel.
It also supports hostnames as well as IP addresses while Ingo Rammer's channel can only handle IPs. 

I upgraded the solution to VS2010 and fixed a few compiler warnings and IPv6-related problems.
TcpEx seem to work well without any further modifications. 

Here is the download link: http://sharp-shooter.ru/misc/TcpExChannel.zip

The original version and dedicated GotDotNet page is also included.

Coordinator
Feb 24, 2011 at 3:43 PM

Great! Many thanks for your support.

This will speed up the release of Zyan 2.0.

I integrate the TcpEx channel as soon as possible.

Coordinator
Feb 25, 2011 at 7:07 AM
Edited Feb 25, 2011 at 7:07 AM

The TcpEx channel is now integrated and checked in into source control (http://zyan.codeplex.com/SourceControl/changeset/changes/9216).

But I´ve found a bug in the TcpEx implementation. It hangs, when it´s configured together with my Crypto Channel Sink.
See the details here: http://zyan.codeplex.com/workitem/677

I think thats no unsolvable problem, but needs a bit of time to fix.

Without encryption, it works fine.

Coordinator
Feb 25, 2011 at 2:59 PM
Edited Feb 25, 2011 at 4:01 PM

>The TcpEx channel is now integrated and checked in

Cool!

>But I've found a bug in the TcpEx implementation. ... Without encryption, it works fine.

I found a couple more issues:

1. Wellknown object urls in TcpExChannel differ from their TcpChannel counterparts: application name is omitted. For example,

<application name="AppServer">
  <service>
    <wellknown mode="Singleton" type="MyApp.Server, ServerImplementation" objectUri="Server.rem"/>
  </service>
</application>
TcpChannel Url:     tcp://localhost:1234/AppServer/Server.rem
TcpExChannel Url:   tcpex://localhost:1234/Server.rem

2. When I misconfigured client app and tried to connect to TcpEx-server with standard TcpChannel, server application crashed with smoke and flames.

Abnormal stop, error report in EventLog (fault in module KERNELBASE.dll, code 0xebf00baa, etc).
So, TcpExChannel is vulnerable: one can just use normal TcpChannel to crash TcpEx-enabled server :)

Coordinator
Feb 25, 2011 at 3:50 PM
Edited Feb 25, 2011 at 4:31 PM

>The TcpEx channel is now integrated and checked in into source control...

I wanted to mention one more thing.

Though TcpExChannel source archive doesn't have explicit license attached, I don't think it is public domain.
The license is most likely either MIT or BSD, as TcpEx was designed as a part of academic research project. 

Please credit the original author (Richard Mason, see TcpExChannel.html).

BTW, GotDotNet page for TcpEx is still available in web archive:
http://web.archive.org/web/20060623220826/http://www.gotdotnet.com/Community/UserSamples/Details.aspx?SampleGuid=3F46C102-9970-48B1-9225-8758C38905B1

Coordinator
Mar 8, 2011 at 10:11 PM
Edited Mar 8, 2011 at 10:11 PM

The interception feature is done. You can get the updated source code here: http://zyan.codeplex.com/SourceControl/changeset/changes/9360

 You can test it with the MiniChat example project. Just paste the following snippet after creation of the ZyanConnection. This snippet checks all chat messages and intercepts them, when the words "fuck" or "sex" are contained.

connection.CallInterceptors.Add(
    new CallInterceptor(typeof(IMiniChat),
    System.Reflection.MemberTypes.Method,
    "SendMessage",
    new Type[] 
    {typeof(string), typeof(string)},
    data =>
    {
        string text2 = (string)data.Parameters[1];

        if (text2.Contains("fuck") || text2.Contains("sex"))
        {
            System.Console.WriteLine("TEXT CONTAINS FORBIDDEN WORDS!");
            data.Intercepted = true;
        }
    }));

connection.CallInterceptionEnabled = true;
Coordinator
Mar 9, 2011 at 10:27 AM

Cool! Thanks a lot!

Is is possible to call intercepted method from CallInterceptionDelegate?

For example:

public interface IServer
{
	// this method will be intercepted
	List<Data> GetHugeList();
}

...

var list1 = proxy.GetHugeList();
var list2 = proxy.GetHugeList(); // this call is handled locally

...

// call interception handler
data =>
{
	if (cachedHugeList == null)
	{
		// something like this (bypass the current interceptor):
		cachedHugeList = (data.Proxy as IServer).GetHugeList();
	}

	data.ReturnValue = localHugeList;
	data.Intercepted = true;
}

Coordinator
Mar 9, 2011 at 7:36 PM

> Is is possible to call intercepted method from CallInterceptionDelegate?

Not yet. But for a caching scenario this absolutly make sense. I´ll add that feature soon.

Coordinator
Mar 9, 2011 at 8:56 PM

Now you can (http://zyan.codeplex.com/SourceControl/changeset/changes/9377)

object returnvalue = data.MakeRemoteCall();
Coordinator
Mar 10, 2011 at 2:07 PM
Edited Mar 10, 2011 at 2:10 PM

Thanks! data.MakeRemoteCall() is even better than data.Proxy property :)

I've added strong-typed fluent builder for call interceptors. Here is the sample:

_connection.CallInterceptors.For<IMiniChat>()
  .Add<string, string>(
    (chat, nickname, text) => chat.SendMessage(nickname, text),
    (data, nickname, text) =>
    {
        if (text.Contains("fuck") || text.Contains("sex"))
        {
            MessageBox.Show("TEXT CONTAINS FORBIDDEN WORDS!");
            data.Intercepted = true;
        }
    });

It works for both Func and Action delegates. Here is the alternative syntax (generic parameters of Add method are inferred from the LINQ expression):

connection.CallInterceptors.For<IMiniChat>().Add(
  (IMiniChat chat, string nickname, string message) => chat.SendMessage(nickname, message),
  (data, nickname, message) =>
  {
    if (message.Contains("fuck") || message.Contains("sex"))
    {
        System.Console.WriteLine("TEXT CONTAINS FORBIDDEN WORDS!");
        data.Intercepted = true;
    }
  });

Calls to Add() can be joined: connection.Interceptors.For<IServer>.Add().Add()...

Note that interception delegate now has the same strong-typed parameters as intercepted method, plus CallInterceptionData parameter.

For Func delegates interception handler must return value, so the compiler could check whether return value has been set.

PS. This update doesn't affect CallInterceptor class at all. The work is done by completely separate helper class which parses LINQ expression, extracts intercepted method name and its parameters, creates call interceptor and adds it to the list. 

Coordinator
Mar 10, 2011 at 11:16 PM

Looks great! This fluent style makes call interception much easier and results in better readability of the code.

Coordinator
Mar 11, 2011 at 11:25 AM

Thanks!

For me, the main advantage is that strong-typed version is refactoring-friendly. For example, renaming SendMessage -> SendText will update all interceptor builders automatically. Adding or removing method parameters will result in compile-time errors, while untyped version will just stop working.

But if you don't have intercepted method metadata at compile-time, untyped interceptor is the only option.