//======================================================================================================
// The library for Custom Remoting via Web Service channel (Sender).
// (C) Copyright 2002, Roman Kiss (rkiss@pathcom.com)
// All rights reserved.
// The code and information is provided "as-is" without waranty of any kind, either expresed or implied.
//------------------------------------------------------------------------------------------------------
// History:
// 22-01-2002 RK Initial Release
//======================================================================================================
//
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Messaging;
using System.Collections;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Formatters.Soap;
using System.Xml;
using System.Xml.Serialization;
using System.Reflection;
using System.Messaging;
using System.Runtime.InteropServices;
namespace RKiss.WebServiceChannelLib
{
public delegate void delegateAsyncWorker(IMessage msgReq, IMessageSink replySink);
// Sender
public class Sender : IChannelSender
{
// these are a hardcoded default values
const int PATH_MINNUMOFFIELDS = 4;
public const string DEFAULT_CHANNELMODE = "Binary";
public const string DEFAULT_CHANNELNAME = "ws";
public const int DEFAULT_CHANNELPRIORITY = 1;
//
private string m_ChannelName;
private int m_ChannelPriority;
private string m_ChannelMode;
// Sender
public Sender() :
this(DEFAULT_CHANNELNAME, DEFAULT_CHANNELMODE, DEFAULT_CHANNELPRIORITY) {}
public Sender(string channelName) :
this(channelName, DEFAULT_CHANNELMODE, DEFAULT_CHANNELPRIORITY) {}
public Sender(string channelName, string mode) :
this(channelName, mode, DEFAULT_CHANNELPRIORITY) {}
public Sender(string channelName, string mode, int priority)
{
Init(channelName, mode, priority);
}
public Sender(IDictionary properties, IServerChannelSinkProvider serverSinkProvider)
{
string channelName = DEFAULT_CHANNELNAME;
int channelPriority = DEFAULT_CHANNELPRIORITY;
string channelMode = DEFAULT_CHANNELMODE;
// administratively setup the config values (xxx.exe.config file)
if(properties.Contains("name"))
channelName = properties["name"].ToString();
if(properties.Contains("priority"))
channelPriority = Convert.ToInt32(properties["priority"]);
if(properties.Contains("mode"))
channelMode = properties["mode"].ToString().ToUpper();
Init(channelName, channelMode, channelPriority);
}
public void Init(string channelName, string mode, int priority)
{
m_ChannelName = channelName;
m_ChannelPriority = priority;
m_ChannelMode = mode;
Trace.WriteLine(string.Format("SenderChannel[{0}] has been initiated.", m_ChannelName));
}
// IChannelSender
public string ChannelName { get { return m_ChannelName; }
}
public int ChannelPriority { get { return m_ChannelPriority; }
}
public string Parse(string url, out string objectURI) { return objectURI = null; }
// IChannelSender (activation)
public virtual IMessageSink CreateMessageSink(String url, Object data, out string objectURI)
{
objectURI = null;
string[] gtwpath = url.Split(new Char[]{';'}); // split gateway/RemoteObject
string[] s = gtwpath[0].Split(new Char[]{'/'});
if(s.Length < PATH_MINNUMOFFIELDS || m_ChannelName + ":" != s[0])
return null; // this is not correct channel
string outpath = gtwpath[0].Replace(m_ChannelName, "http"); // web service
objectURI = gtwpath[1].TrimStart(new char[]{' '}); // remote object
WSMessageSink msgsink = new WSMessageSink(m_ChannelMode, objectURI, outpath);
Trace.WriteLine(string.Format("SenderChannel[{0}] initiated the WSMessageSink for {1}.", m_ChannelName, objectURI));
return msgsink;
}
}//Sender
// MSMQMessageSink
public class WSMessageSink : IMessageSink, IDictionary
{
private string m_ObjectUri;
private string m_outpath;
private string m_mode;
// MSMQMessageSink
public WSMessageSink(string mode, string objuri, string outpath)
{
m_mode = mode;
m_ObjectUri = objuri;
m_outpath = outpath;
}
// helper
// handler of the AsyncProcessMessage worker
private void handlerAsyncWorker(IMessage msgReq, IMessageSink replySink)
{
AsyncResult ar = replySink.NextSink as AsyncResult;
// call Remote Method and wait for its Return Value
IMessage msgRsp = SyncProcessMessage(msgReq);
// update AsyncResult state (_replyMsg, IsCompleted, waitstate, etc.)
replySink.SyncProcessMessage(msgRsp);
}
// IMessageSink (MethodCall)
public virtual IMessage SyncProcessMessage(IMessage msgReq)
{
IMessage msgRsp = null;
try
{
msgReq.Properties["__Uri"] = m_ObjectUri;
Service webservice = new Service(m_outpath);
if(m_mode == "SOAP")
{
// serialize IMessage into the stream (SoapMessage)
MemoryStream reqstream = new MemoryStream();
SoapFormatter sf = new SoapFormatter();
RemotingSurrogateSelector rss = new RemotingSurrogateSelector();
rss.SetRootObject(msgReq);
sf.SurrogateSelector = rss;
sf.AssemblyFormat = FormatterAssemblyStyle.Full;
sf.TypeFormat = FormatterTypeStyle.TypesAlways;
sf.TopObject = new SoapMessage();
sf.Serialize(reqstream, msgReq);
ISoapMessage sm = sf.TopObject;
reqstream.Position = 0;
StreamReader sr = new StreamReader(reqstream);
string request = sr.ReadToEnd();
reqstream.Close();
sr.Close();
// call web service
string respond = webservice.SyncProcessSoapMessage(request);
// return messages
StreamWriter rspsw = new StreamWriter(new MemoryStream());
rspsw.Write(respond);
rspsw.Flush();
rspsw.BaseStream.Position = 0;
ISoapMessage rspsoapmsg = (ISoapMessage)sf.Deserialize(rspsw.BaseStream);
rspsw.Close();
if(rspsoapmsg.ParamValues[0] is Exception)
{
throw rspsoapmsg.ParamValues[0] as Exception;
}
else
{
object returnVal = rspsoapmsg.ParamValues[4];
object[] OutArgs = rspsoapmsg.ParamValues[5] as object[];
LogicalCallContext lcc = rspsoapmsg.ParamValues[6] as LogicalCallContext;
ReturnMessage rm = new ReturnMessage(
returnVal, //Object return
OutArgs, //Object[] outArgs
OutArgs.Length, //int outArgsCount
lcc, //LogicalCallContext callCtx
msgReq as IMethodCallMessage //IMethodCallMessage mcm
);
msgRsp = rm as IMessage;
}
}
else
{
msgReq.Properties["__Uri2"] = m_ObjectUri; // workaround!
// serialize and encode IMessage
BinaryFormatter bf = new BinaryFormatter();
MemoryStream reqstream = new MemoryStream();
bf.Serialize(reqstream, msgReq);
reqstream.Position = 0;
string request = Convert.ToBase64String(reqstream.ToArray());
reqstream.Close();
// call Web Service
string respond = webservice.SyncProcessMessage(request);
// decode and deserialize IMessage
byte[] rspbyteArray = Convert.FromBase64String(respond);
MemoryStream rspstream = new MemoryStream();
rspstream.Write(rspbyteArray, 0, rspbyteArray.Length);
rspstream.Position = 0;
msgRsp = (IMessage)bf.Deserialize(rspstream);
rspstream.Close();
}
}
catch(Exception ex)
{
Trace.WriteLine(string.Format("Client:SyncProcessMessage error = {0}", ex.Message));
msgRsp = new ReturnMessage(ex, (IMethodCallMessage)msgReq);
}
return msgRsp;
}
public virtual IMessageCtrl AsyncProcessMessage(IMessage msgReq, IMessageSink replySink)
{
IMessageCtrl imc = null;
if(replySink == null) // OneWayAttribute
{
Trace.WriteLine("Client-[OneWay]Async:CALL");
SyncProcessMessage(msgReq);
}
else
{
Trace.WriteLine("Client-Async:CALL");
// spawn thread (delegate work)
delegateAsyncWorker daw = new delegateAsyncWorker(handlerAsyncWorker);
daw.BeginInvoke(msgReq, replySink, null, null);
}
return imc;
}
// IDictionary (not implemented)
public virtual IMessageSink NextSink { get { return null; }}
public virtual bool Contains(Object key) { return false; }
public virtual void Add(Object key, Object value){}
public virtual void Remove(Object key){}
public virtual void Clear() {}
public virtual IDictionaryEnumerator GetEnumerator() { return null; }
IEnumerator IEnumerable.GetEnumerator() { return null; }
public int Count { get { return 0; }}
public void CopyTo(Array array, int index){}
public ICollection Keys { get { return null; }}
public ICollection Values { get { return null; }}
public Object SyncRoot { get { return null; }}
public bool IsReadOnly { get { return true; }}
public bool IsFixedSize { get { return true; }}
public bool IsSynchronized { get { return true; }}
public Object this[Object key] { get { return null; } set {}}
}// WSMessageSink
}//namespace RKiss.WebServiceChannelLib