using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Security;
+using System.Threading;
+using System.Xml;
namespace System.ServiceModel
{
-#if TARGET_DOTNET
- [MonoTODO]
- public
+#if NET_2_1
+ internal class DuplexClientRuntimeChannel
+ {
+ }
#else
- internal
+ internal class DuplexClientRuntimeChannel
+ : ClientRuntimeChannel, IDuplexContextChannel
+ {
+ public DuplexClientRuntimeChannel (ServiceEndpoint endpoint,
+ ChannelFactory factory, EndpointAddress remoteAddress, Uri via)
+ : base (endpoint, factory, remoteAddress, via)
+ {
+ }
+
+ public bool AutomaticInputSessionShutdown {
+ get { throw new NotImplementedException (); }
+ set { throw new NotImplementedException (); }
+ }
+
+ public InstanceContext CallbackInstance { get; set; }
+
+ Action<TimeSpan> session_shutdown_delegate;
+
+ public void CloseOutputSession (TimeSpan timeout)
+ {
+ throw new NotImplementedException ();
+ }
+
+ public IAsyncResult BeginCloseOutputSession (TimeSpan timeout, AsyncCallback callback, object state)
+ {
+ if (session_shutdown_delegate == null)
+ session_shutdown_delegate = new Action<TimeSpan> (CloseOutputSession);
+ return session_shutdown_delegate.BeginInvoke (timeout, callback, state);
+ }
+
+ public void EndCloseOutputSession (IAsyncResult result)
+ {
+ session_shutdown_delegate.EndInvoke (result);
+ }
+ }
#endif
- class ClientRuntimeChannel
+
+ internal class ClientRuntimeChannel
: CommunicationObject, IClientChannel
{
ClientRuntime runtime;
- ChannelFactory factory;
- IRequestChannel request_channel;
- IOutputChannel output_channel;
+ EndpointAddress remote_address;
+ ContractDescription contract;
+ MessageVersion message_version;
+ TimeSpan default_open_timeout, default_close_timeout;
+ IChannel channel;
+ IChannelFactory factory;
+
+ #region delegates
readonly ProcessDelegate _processDelegate;
delegate object ProcessDelegate (MethodBase method, string operationName, object [] parameters);
- public ClientRuntimeChannel (ClientRuntime runtime,
- ChannelFactory factory)
+ readonly RequestDelegate requestDelegate;
+
+ delegate Message RequestDelegate (Message msg, TimeSpan timeout);
+
+ readonly SendDelegate sendDelegate;
+
+ delegate void SendDelegate (Message msg, TimeSpan timeout);
+ #endregion
+
+ public ClientRuntimeChannel (ServiceEndpoint endpoint,
+ ChannelFactory channelFactory, EndpointAddress remoteAddress, Uri via)
+ : this (endpoint.CreateRuntime (), endpoint.Contract, channelFactory.DefaultOpenTimeout, channelFactory.DefaultCloseTimeout, null, channelFactory.OpenedChannelFactory, endpoint.Binding.MessageVersion, remoteAddress, via)
+ {
+ }
+
+ public ClientRuntimeChannel (ClientRuntime runtime, ContractDescription contract, TimeSpan openTimeout, TimeSpan closeTimeout, IChannel contextChannel, IChannelFactory factory, MessageVersion messageVersion, EndpointAddress remoteAddress, Uri via)
{
this.runtime = runtime;
- this.factory = factory;
+ this.remote_address = remoteAddress;
+ runtime.Via = via;
+ this.contract = contract;
+ this.message_version = messageVersion;
+ default_open_timeout = openTimeout;
+ default_close_timeout = closeTimeout;
_processDelegate = new ProcessDelegate (Process);
+ requestDelegate = new RequestDelegate (Request);
+ sendDelegate = new SendDelegate (Send);
+
+ // default values
+ AllowInitializationUI = true;
+ OperationTimeout = TimeSpan.FromMinutes (1);
+
+ if (contextChannel != null)
+ channel = contextChannel;
+ else {
+ var method = factory.GetType ().GetMethod ("CreateChannel", new Type [] {typeof (EndpointAddress), typeof (Uri)});
+ channel = (IChannel) method.Invoke (factory, new object [] {remote_address, Via});
+ this.factory = factory;
+ }
}
public ClientRuntime Runtime {
get { return runtime; }
}
- #region IClientChannel
+ IRequestChannel RequestChannel {
+ get { return channel as IRequestChannel; }
+ }
- [MonoTODO]
- public bool AllowInitializationUI {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
+ IOutputChannel OutputChannel {
+ get { return channel as IOutputChannel; }
}
- [MonoTODO]
+ #region IClientChannel
+
+ bool did_interactive_initialization;
+
+ public bool AllowInitializationUI { get; set; }
+
public bool DidInteractiveInitialization {
- get { throw new NotImplementedException (); }
+ get { return did_interactive_initialization; }
}
public Uri Via {
get { return runtime.Via; }
}
- [MonoTODO]
+ class DelegatingWaitHandle : WaitHandle
+ {
+ public DelegatingWaitHandle (IAsyncResult [] results)
+ {
+ this.results = results;
+ }
+
+ IAsyncResult [] results;
+
+ protected override void Dispose (bool disposing)
+ {
+ if (disposing)
+ foreach (var r in results)
+ r.AsyncWaitHandle.Close ();
+ }
+
+ public override bool WaitOne ()
+ {
+ foreach (var r in results)
+ r.AsyncWaitHandle.WaitOne ();
+ return true;
+ }
+
+ public override bool WaitOne (int millisecondsTimeout)
+ {
+ return WaitOne (millisecondsTimeout, false);
+ }
+
+ WaitHandle [] ResultWaitHandles {
+ get {
+ var arr = new WaitHandle [results.Length];
+ for (int i = 0; i < arr.Length; i++)
+ arr [i] = results [i].AsyncWaitHandle;
+ return arr;
+ }
+ }
+
+ public override bool WaitOne (int millisecondsTimeout, bool exitContext)
+ {
+ return WaitHandle.WaitAll (ResultWaitHandles, millisecondsTimeout, exitContext);
+ }
+
+ public override bool WaitOne (TimeSpan timeout, bool exitContext)
+ {
+ return WaitHandle.WaitAll (ResultWaitHandles, timeout, exitContext);
+ }
+ }
+
+ class DisplayUIAsyncResult : IAsyncResult
+ {
+ public DisplayUIAsyncResult (IAsyncResult [] results)
+ {
+ this.results = results;
+ }
+
+ IAsyncResult [] results;
+
+ internal IAsyncResult [] Results {
+ get { return results; }
+ }
+
+ public object AsyncState {
+ get { return null; }
+ }
+
+ WaitHandle wait_handle;
+
+ public WaitHandle AsyncWaitHandle {
+ get {
+ if (wait_handle == null)
+ wait_handle = new DelegatingWaitHandle (results);
+ return wait_handle;
+ }
+ }
+
+ public bool CompletedSynchronously {
+ get {
+ foreach (var r in results)
+ if (!r.CompletedSynchronously)
+ return false;
+ return true;
+ }
+ }
+ public bool IsCompleted {
+ get {
+ foreach (var r in results)
+ if (!r.IsCompleted)
+ return false;
+ return true;
+ }
+ }
+ }
+
public IAsyncResult BeginDisplayInitializationUI (
AsyncCallback callback, object state)
{
- throw new NotImplementedException ();
+ OnInitializationUI ();
+ IAsyncResult [] arr = new IAsyncResult [runtime.InteractiveChannelInitializers.Count];
+ int i = 0;
+ foreach (var init in runtime.InteractiveChannelInitializers)
+ arr [i++] = init.BeginDisplayInitializationUI (this, callback, state);
+ return new DisplayUIAsyncResult (arr);
}
- [MonoTODO]
public void EndDisplayInitializationUI (
IAsyncResult result)
{
- throw new NotImplementedException ();
+ DisplayUIAsyncResult r = (DisplayUIAsyncResult) result;
+ int i = 0;
+ foreach (var init in runtime.InteractiveChannelInitializers)
+ init.EndDisplayInitializationUI (r.Results [i++]);
+
+ did_interactive_initialization = true;
}
- [MonoTODO]
public void DisplayInitializationUI ()
{
- throw new NotImplementedException ();
+ OnInitializationUI ();
+ foreach (var init in runtime.InteractiveChannelInitializers)
+ init.EndDisplayInitializationUI (init.BeginDisplayInitializationUI (this, null, null));
+
+ did_interactive_initialization = true;
+ }
+
+ void OnInitializationUI ()
+ {
+ if (!AllowInitializationUI && runtime.InteractiveChannelInitializers.Count > 0)
+ throw new InvalidOperationException ("AllowInitializationUI is set to false but the client runtime contains one or more InteractiveChannelInitializers.");
}
public void Dispose ()
#region IContextChannel
[MonoTODO]
- public bool AllowOutputBatching {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
- }
+ public bool AllowOutputBatching { get; set; }
- [MonoTODO]
public IInputSession InputSession {
- get { throw new NotImplementedException (); }
+ get {
+ ISessionChannel<IInputSession> ch = RequestChannel as ISessionChannel<IInputSession>;
+ ch = ch ?? OutputChannel as ISessionChannel<IInputSession>;
+ if (ch != null)
+ return ch.Session;
+ var dch = OutputChannel as ISessionChannel<IDuplexSession>;
+ return dch != null ? dch.Session : null;
+ }
}
- [MonoTODO]
public EndpointAddress LocalAddress {
- get { throw new NotImplementedException (); }
+ get {
+ var dc = OperationChannel as IDuplexChannel;
+ return dc != null ? dc.LocalAddress : null;
+ }
}
[MonoTODO]
- public TimeSpan OperationTimeout {
- get { throw new NotImplementedException (); }
- set { throw new NotImplementedException (); }
- }
+ public TimeSpan OperationTimeout { get; set; }
- [MonoTODO]
public IOutputSession OutputSession {
- get { throw new NotImplementedException (); }
+ get {
+ ISessionChannel<IOutputSession> ch = RequestChannel as ISessionChannel<IOutputSession>;
+ ch = ch ?? OutputChannel as ISessionChannel<IOutputSession>;
+ if (ch != null)
+ return ch.Session;
+ var dch = OutputChannel as ISessionChannel<IDuplexSession>;
+ return dch != null ? dch.Session : null;
+ }
}
- [MonoTODO]
public EndpointAddress RemoteAddress {
- get { throw new NotImplementedException (); }
+ get { return RequestChannel != null ? RequestChannel.RemoteAddress : OutputChannel.RemoteAddress; }
}
- [MonoTODO]
public string SessionId {
- get { throw new NotImplementedException (); }
+ get { return OutputSession != null ? OutputSession.Id : InputSession != null ? InputSession.Id : null; }
}
#endregion
// CommunicationObject
protected internal override TimeSpan DefaultOpenTimeout {
- get { return factory.DefaultOpenTimeout; }
+ get { return default_open_timeout; }
}
protected internal override TimeSpan DefaultCloseTimeout {
- get { return factory.DefaultCloseTimeout; }
+ get { return default_close_timeout; }
}
protected override void OnAbort ()
{
- factory.Abort ();
+ channel.Abort ();
+ if (factory != null)
+ factory.Abort ();
}
+ Action<TimeSpan> close_delegate;
+
protected override IAsyncResult OnBeginClose (
TimeSpan timeout, AsyncCallback callback, object state)
{
- return factory.BeginClose (timeout, callback, state);
+ if (close_delegate == null)
+ close_delegate = new Action<TimeSpan> (OnClose);
+ return close_delegate.BeginInvoke (timeout, callback, state);
}
protected override void OnEndClose (IAsyncResult result)
{
- factory.EndClose (result);
+ close_delegate.EndInvoke (result);
}
protected override void OnClose (TimeSpan timeout)
{
- factory.Close (timeout);
+ DateTime start = DateTime.Now;
+ channel.Close (timeout);
+ if (factory != null)
+ factory.Close (timeout - (DateTime.Now - start));
}
protected override IAsyncResult OnBeginOpen (
protected override void OnOpen (TimeSpan timeout)
{
+ if (runtime.InteractiveChannelInitializers.Count > 0 && !DidInteractiveInitialization)
+ throw new InvalidOperationException ("The client runtime is assigned interactive channel initializers, and in such case DisplayInitializationUI must be called before the channel is opened.");
}
// IChannel
+
+ IChannel OperationChannel {
+ get { return channel; }
+ }
+
public T GetProperty<T> () where T : class
{
- return factory.GetProperty<T> ();
+ return OperationChannel.GetProperty<T> ();
}
// IExtensibleObject<IContextChannel>
#region Request/Output processing
- public IAsyncResult BeginProcess (MethodBase method, string operationName, object [] parameters) {
- object [] param = new object [parameters.Length - 2];
- for (int i = 0; i < param.Length; i++)
- param [i] = parameters [i];
- return _processDelegate.BeginInvoke (method, operationName, param, (AsyncCallback) parameters [parameters.Length - 2], parameters [parameters.Length - 1]);
+ public IAsyncResult BeginProcess (MethodBase method, string operationName, object [] parameters, AsyncCallback callback, object asyncState)
+ {
+ return _processDelegate.BeginInvoke (method, operationName, parameters, callback, asyncState);
}
- public object EndProcess (MethodBase method, string operationName, object [] parameters) {
- return _processDelegate.EndInvoke ((IAsyncResult) parameters [0]);
+ public object EndProcess (MethodBase method, string operationName, object [] parameters, IAsyncResult result)
+ {
+ if (result == null)
+ throw new ArgumentNullException ("result");
+ if (parameters == null)
+ throw new ArgumentNullException ("parameters");
+ // FIXME: the method arguments should be verified to be
+ // identical to the arguments in the corresponding begin method.
+ return _processDelegate.EndInvoke (result);
}
public object Process (MethodBase method, string operationName, object [] parameters)
{
+ try {
+ return DoProcess (method, operationName, parameters);
+ } catch (Exception ex) {
+ Console.Write ("Exception in async operation: ");
+ Console.WriteLine (ex);
+ throw;
+ }
+ }
+
+ object DoProcess (MethodBase method, string operationName, object [] parameters)
+ {
+ if (AllowInitializationUI)
+ DisplayInitializationUI ();
OperationDescription od = SelectOperation (method, operationName, parameters);
if (!od.IsOneWay)
return Request (od, parameters);
operation = Runtime.OperationSelector.SelectOperation (method, parameters);
else
operation = operationName;
- OperationDescription od = factory.Endpoint.Contract.Operations.Find (operation);
+ OperationDescription od = contract.Operations.Find (operation);
if (od == null)
throw new Exception (String.Format ("OperationDescription for operation '{0}' was not found in its internally-generated contract.", operation));
return od;
}
- BindingParameterCollection CreateBindingParameters ()
- {
- BindingParameterCollection pl =
- new BindingParameterCollection ();
-
- ContractDescription cd = factory.Endpoint.Contract;
-#if !NET_2_1
- pl.Add (ChannelProtectionRequirements.CreateFromContract (cd));
-
- foreach (IEndpointBehavior behavior in factory.Endpoint.Behaviors)
- behavior.AddBindingParameters (factory.Endpoint, pl);
-#endif
-
- return pl;
- }
-
- void SetupOutputChannel ()
- {
- if (output_channel != null)
- return;
- BindingParameterCollection pl =
- CreateBindingParameters ();
- bool session = false;
- switch (factory.Endpoint.Contract.SessionMode) {
- case SessionMode.Required:
- session = factory.Endpoint.Binding.CanBuildChannelFactory<IOutputSessionChannel> (pl);
- if (!session)
- throw new InvalidOperationException ("The contract requires session support, but the binding does not support it.");
- break;
- case SessionMode.Allowed:
- session = !factory.Endpoint.Binding.CanBuildChannelFactory<IOutputChannel> (pl);
- break;
- }
-
- EndpointAddress address = factory.Endpoint.Address;
- Uri via = Runtime.Via;
-
- if (session) {
- IChannelFactory<IOutputSessionChannel> f =
- factory.Endpoint.Binding.BuildChannelFactory<IOutputSessionChannel> (pl);
- f.Open ();
- output_channel = f.CreateChannel (address, via);
- } else {
- IChannelFactory<IOutputChannel> f =
- factory.Endpoint.Binding.BuildChannelFactory<IOutputChannel> (pl);
- f.Open ();
- output_channel = f.CreateChannel (address, via);
- }
-
- output_channel.Open ();
- }
-
- void SetupRequestChannel ()
- {
- if (request_channel != null)
- return;
-
- BindingParameterCollection pl =
- CreateBindingParameters ();
- bool session = false;
- switch (factory.Endpoint.Contract.SessionMode) {
- case SessionMode.Required:
- session = factory.Endpoint.Binding.CanBuildChannelFactory<IRequestSessionChannel> (pl);
- if (!session)
- throw new InvalidOperationException ("The contract requires session support, but the binding does not support it.");
- break;
- case SessionMode.Allowed:
- session = !factory.Endpoint.Binding.CanBuildChannelFactory<IRequestChannel> (pl);
- break;
- }
-
- EndpointAddress address = factory.Endpoint.Address;
- Uri via = Runtime.Via;
-
- if (session) {
- IChannelFactory<IRequestSessionChannel> f =
- factory.Endpoint.Binding.BuildChannelFactory<IRequestSessionChannel> (pl);
- f.Open ();
- request_channel = f.CreateChannel (address, via);
- } else {
- IChannelFactory<IRequestChannel> f =
- factory.Endpoint.Binding.BuildChannelFactory<IRequestChannel> (pl);
- f.Open ();
- request_channel = f.CreateChannel (address, via);
- }
-
- request_channel.Open ();
- }
-
void Output (OperationDescription od, object [] parameters)
{
- SetupOutputChannel ();
+ if (OutputChannel.State != CommunicationState.Opened)
+ OutputChannel.Open ();
ClientOperation op = runtime.Operations [od.Name];
- Output (CreateRequest (op, parameters));
+ Send (CreateRequest (op, parameters), OperationTimeout);
}
object Request (OperationDescription od, object [] parameters)
{
- SetupRequestChannel ();
+ if (OperationChannel.State != CommunicationState.Opened)
+ OperationChannel.Open ();
ClientOperation op = runtime.Operations [od.Name];
object [] inspections = new object [runtime.MessageInspectors.Count];
for (int i = 0; i < inspections.Length; i++)
inspections [i] = runtime.MessageInspectors [i].BeforeSendRequest (ref req, this);
- Message res = Request (req);
+ Message res = Request (req, OperationTimeout);
if (res.IsFault) {
MessageFault fault = MessageFault.CreateFault (res, runtime.MaxFaultSize);
if (fault.HasDetail && fault is MessageFault.SimpleMessageFault) {
return res;
}
- Message Request (Message msg)
+ #region Message-based Request() and Send()
+ // They are internal for ClientBase<T>.ChannelBase use.
+ internal Message Request (Message msg, TimeSpan timeout)
+ {
+ if (RequestChannel != null)
+ return RequestChannel.Request (msg, timeout);
+ else {
+ DateTime startTime = DateTime.Now;
+ OutputChannel.Send (msg, timeout);
+ return ((IDuplexChannel) OutputChannel).Receive (timeout - (DateTime.Now - startTime));
+ }
+ }
+
+ internal IAsyncResult BeginRequest (Message msg, TimeSpan timeout, AsyncCallback callback, object state)
+ {
+ return requestDelegate.BeginInvoke (msg, timeout, callback, state);
+ }
+
+ internal Message EndRequest (IAsyncResult result)
{
- return request_channel.Request (msg, factory.Endpoint.Binding.SendTimeout);
+ return requestDelegate.EndInvoke (result);
}
- void Output (Message msg)
+ internal void Send (Message msg, TimeSpan timeout)
{
- output_channel.Send (msg, factory.Endpoint.Binding.SendTimeout);
+ OutputChannel.Send (msg, timeout);
}
+ internal IAsyncResult BeginSend (Message msg, TimeSpan timeout, AsyncCallback callback, object state)
+ {
+ return sendDelegate.BeginInvoke (msg, timeout, callback, state);
+ }
+
+ internal void EndSend (IAsyncResult result)
+ {
+ sendDelegate.EndInvoke (result);
+ }
+ #endregion
+
Message CreateRequest (ClientOperation op, object [] parameters)
{
- MessageVersion version = factory.Endpoint.Binding.MessageVersion;
+ MessageVersion version = message_version;
if (version == null)
version = MessageVersion.Default;
+ Message msg;
if (op.SerializeRequest)
- return op.GetFormatter ().SerializeRequest (
+ msg = op.GetFormatter ().SerializeRequest (
version, parameters);
else
- return (Message) parameters [0];
+ msg = (Message) parameters [0];
+
+ if (OutputSession != null)
+ msg.Headers.MessageId = new UniqueId (OutputSession.Id);
+ msg.Properties.AllowOutputBatching = AllowOutputBatching;
+
+ return msg;
}
#endregion